每天学习一个命令:apt 安装卸载软件

APT 是 Advanced Packaging Tools 的缩写,是 Debian 及其派生的发行版(使用最广泛的 Ubuntu/Linux Mint 等等)的软件包管理器。 APT 可以自动下载、配置和安装二进制或源代码格式软件包,简化了 Unix 系统上管理软件的过程。 APT 最早被设计成 dpkg 的前端,用来处理 deb 格式的软件包。

APT 主要由以下几个命令组成:

  • apt-get
  • apt-cache
  • apt-file

APT 能够自动处理软件的依赖问题,大大简化了工作流程

这里主要讲 apt-get 的常见用法

常用命令

搜索软件包

apt search package_name

安装软件包

apt install package

更新源

自动根据 /etc/apt/sources.list 中的设定,以及 /etc/apt/sources.list.d 目录下的设定,更新软件资料源。

sudo apt update

使用如下命令安装更新

sudo apt upgrade

移除软件源

apt remove package  # 移除软件包,但是保留配置文件
apt purge package #移除软件包并移除配置
apt autoremove # 移除孤立的并不被依赖的软件包

列出软件清单

apt list

升级系统

sudo apt-get dist-upgrade

最后如果还有什么不明了的,可以 man apk

前端界面

我目前使用

  • Synaptic

reference


2016-01-11 apt , linux , command , ubuntu

boost 学习笔记 6:容器

对应书中的第七章容器与数据结构。先是讲了五个容器: array , dynamic_bitset, unordered, bimap和 circular_buffer. 他们都是对标准容器的扩充。

  • array 是对 C++内建数组的简单包装
  • dynamic_bitset 可容纳任意数量的二进制位
  • unordered 实现散列容器,非二叉树实现
  • bimap 是双向 map 扩展了std::map 的内容
  • circular_buffer 是循环队列。

tuple, any 和 variant 能够容纳不同类型容器

  • tuple 是对 std::pair 的泛化
  • any和 variant 都只能容纳一个可变类型的容器。any 可以持有任何类型数据,variant 需要在编译期指定类型。

最后 multi_arrayproperty_tree 两个组件使用组合模式实现复杂的数据结构。 multi_array 是一个泛型多维容器, property_tree 出自 1.41 版,可以解析 xml, son, ini 和 info 四种格式文件,在内部构造属性树。

对容纳元素基本要求:析构函数不能抛出异常

array

数组容器,包装了 C++ 内建数组,为其提供标准 STL 容器接口,begin(), front() 等等,性能与原始数组相差不大。但是 array 功能有限,当需要可变容量数组时,还是需要借助 std::vector 或者 boost::scoped_array

array 主要缺陷:

  • 没有构造函数,不能指定大小和初值
  • 没有 push_back() 和 push_front() ,不能动态增长
  • 不能搭配插入迭代器适配器功能

需包含头文件

#include <boost/array.hpp>
using namespace boost;

具体用法:

#include <iostream>
#include <boost/array.hpp>
#include <boost/typeof/typeof.hpp>
using namespace boost;

int main(int argc, const char * argv[]) {
    // 赋值
    array<int, 5> myArray;
    myArray[1] = 2;
    myArray.front() = 1;
    myArray.back() = 5;

    // 获取指针
    int* p = myArray.c_array();
    *(p+2) = 3;

    for (array<int,5>::iterator itr = myArray.begin(); itr != myArray.end(); ++itr) {
        std::cout << *itr << std::endl;
    }

    array<int, 10> arr2;
    arr2.assign(5);
    for (BOOST_AUTO(pos, arr2.begin()); pos != arr2.end(); ++pos) {
        std::cout << *pos << ",";
    }

    // 类似数组的初始化
    array<int, 3> ar3= { 1, 2, 3 };
    array<int, 10> ar4 = {0};           // all element equal 0
    array<std::string, 5> ar5= {"first"};  // 初始化第一个元素

    return 0;
}

dynamic_bitset

C++98 标准为处理二进制提供了两个工具: vector 和 bitset. 他们各自有优缺点: vector 可以动态增长,但不能方便地进行位运算;而 bitset 则刚好相反,可以方便地对容纳的二进制做位运算,但不能动态增长。 dynamic_bitset 正好填补了两者之间的空白。

需包含如下头文件

#include <boost/dynamic_bitset.hpp>
using namespace boost;

unordered

unordered 库提供两个散列集合类 unordered_setunordered_multiset ,同样 STLport 库也提供 hash_set 和 hash_multiset. 他们接口、用法和 STL 中标准关联容器 set/multiset 相同,只是内部使用散列代替二叉树实现,因此查找复杂度由对数降为常数。

散列容器 hash container 比二叉树的存储方式可以提供更高的访问效率。boost.unordered 库提供了一个完全符合 C++ 新标准草案 TR1 的散列容器实现,包括无序集合 set 和 无序映射 map.

unordered 库提供两个散列映射类 unordered_mapunordered_multimap ,同样 STLport 中也提供了 hash_map/hash_multimap. 他们的接口、用法和 STL 中的标准关联容器 map/multimap 相同,只是内部使用散列表代替了二叉树。

#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
using namespace boost;

代码:

#include <iostream>
#include <string>
#include <boost/assign.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
using namespace boost;
using namespace std;

int main(int argc, const char * argv[]) {
    // 散列集合类
    using namespace boost::assign;
    using namespace boost::unordered;

    // 散列集合
    unordered_set<string> s = (list_of<string>("one")("two")("three"));
    s.insert("four");
    s.erase("two");
    unordered_set<string>::iterator it = s.find("one");
    s.erase(it);
    for (unordered_set<string>::iterator itr = s.begin(); itr != s.end(); ++itr) {
        cout << *itr << endl;
    }
    s.empty();


    // 关联式容器
    unordered_map<int, std::string> um = map_list_of(1,"one")(2,"two")(3,"three");
    um.insert(std::make_pair(10,"ten"));
    um[11] = "eleven";
    um[15] = "fifteen";

    std::cout << um[15] << std::endl;
    BOOST_AUTO(p, um.begin());
    for (p; p!= um.end(); ++p) {
        cout << p->first << "-" << p->second << ",";
    }
    cout << endl;

    um.erase(11);               // delete key 11
    cout << um.size() << endl;

    // stl map
    map<int, std::string> stdmap;
    // assignment
    stdmap.insert(map<int, std::string>::value_type(1, "one"));
    stdmap.insert(std::pair<int, std::string>(2, "two"));
    stdmap.insert(std::make_pair(3, "three"));
    stdmap[4] = "four";

    map<int, std::string>::iterator pos;
    for (pos = stdmap.begin(); pos != stdmap.end(); ++pos) {
        std::cout<< "key: " << pos->first << "\t"
            << "value: " << pos->second << "\n";
    }

    stdmap.erase(4);
    cout << "map size " << stdmap.size() << endl;

    return 0;
}

bimap

可以容纳两个类型的元素,类似于 C++标准提供的映射型容器 map 和 multi_map ,但是 C++标准中的关联数组,只是将一个 key 映射到一个 value, 这种关系是单向的,只能从 key 到 value 而不能反过来。因此 boost.bimap 扩展了标准库的映射型容器,提供双向映射的能力。

对于 bimap<X, Y> , bimap.let 相当于 map<X, Y> ,而 bimap.right 相当于 map<Y, X>

bimap<collection_type_of<X>, collection_type_of<Y> >

这个概念下, std::map 和 std::multi_map 的左值是一个有序的集合,而右组无任何约束,对应的 bimap 是:

bimap< set_of<X> , unconstrained_set_of<Y> > 和 bimap<multiset_of<X>, unconstrained_set_of<Y> >

bimap 定义的集合类型包括如下:

  • set_of 可以作为键值 索引,有序且唯一,视图相当于 map
  • multiset_of 可以用作键值 索引,有序不唯一 ,视图相当于 multimap
  • unordered_set_of 可以作为键值,无序且唯一,视图相当于 unordered_map
  • unordered_multiset_of 可以作为键值,无序不唯一,视图相当于 unordered_multimap
  • list_of 不能作为键值,序列集合,无对应 STL 容器
  • vector_of 不能作为键值,随机访问集合,无对应 STL 容器
  • unconstrained_set_of 不能作为键值,无任何约束关系,无对应 STL 容器

bimap 可以使用 using namespace tags; tagged类给左组和右组数据在语法层面加上“标签”。标签的定义如下:

template< class Type, class Tag>
struct tagged {
    typedef Type value_type;
    typedef Tag tag;
};

tagged<int , struct id> 表示包装了 int 类型 ,标签名字是 id.

#include <iostream>
#include <string>
#include <boost/assign.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/bimap.hpp>
#include <boost/bimap/list_of.hpp>
#include <boost/bimap/unordered_set_of.hpp>
#include <boost/bimap/multiset_of.hpp>
#include <boost/bimap/list_of.hpp>
#include <boost/bimap/vector_of.hpp>
#include <boost/bimap/unconstrained_set_of.hpp>
#include <boost/assign.hpp>
using namespace boost;
using namespace std;

template< typename T>
void print_map(T& m){
    for (BOOST_AUTO(pos, m.begin()); pos != m.end(); ++pos) {
        cout << pos->first << "<-->" << pos->second << endl;
    }
}

int main(int argc, const char * argv[]) {

    using namespace boost::bimaps;
    using namespace tags;
    // bimap
    bimap<int, string> bmap;

    bmap.left.insert(make_pair(1, "one"));

    bmap.right.insert(make_pair("two", 2));

    for (BOOST_AUTO(pos, bmap.left.begin()); pos != bmap.left.end(); ++pos) {
        cout << "left key: " << pos->first << " value: " << pos->second << endl;
    }

    // 类型集合位于名字空间 boost::bimaps, 需包含各自同名头文件,文件位于 <boost/bimap/list_of.hpp>
    // 左组是有序集合,右组是无序集合
    bimap<int, unordered_set_of< string > > bm1;

    // 左组和右组都是有序多值集合
    bimap< multiset_of<int> , multiset_of< string > > bm2;

    // 左组是无序集合,右组是一个序列,只能有左视图
    bimap< unordered_set_of<int> , list_of<string> > bm3;

    // 左组随机访问集合,右组无约束
    bimap< vector_of<int>, unconstrained_set_of< string > > bm4;

    bimap<tagged<int, struct id>, tagged<string, struct name> > bm;
    bm.by<id>().insert(make_pair(1, "Sam"));

    bm.by<name>().insert(make_pair("Tom", 2));

    print_map(bm.by<id>());

    typedef bimap<int, string> bm_t;

    using namespace boost::assign;
    bm_t bmt = assign::list_of<bm_t::relation>(1, "one")(2 , "two");

    BOOST_AUTO(pos, bmt.left.find(2));
    cout << "key: " << pos->first << "\t" << "value: "<< pos->second << endl;
    BOOST_AUTO(pos2, bmt.right.find("one"));
    cout << "key: " << pos2->first << "\t" << "value: " << pos2->second << endl;

    bmt.left.replace_key(pos, 22);
    bmt.left.replace_data(pos, "replaced two");

    print_map(bmt.left);

    return 0;
}

bimap 还有很多高级用法,等用到的时候查阅文档吧。

circular_buffer

循环缓冲区数据结构,大小固定的循环队列。固定大小的缓存容量,当新增加的元素超过缓冲区的容量时,自动将最初的元素覆盖。

tuple

tuple 元组 定义了固定数目元素的容器,其中每个元素类型都可以不相同,这与其他容器有本质的区别。

#include <iostream>
#include <string>
#include <boost/assign.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/assign.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/tuple/tuple_io.hpp>
using namespace boost;
using namespace std;

int main(int argc, const char * argv[]) {

    typedef boost::tuple<int, string, double> my_tuple;
    my_tuple tuple0(1,"test");
    my_tuple tuple1 = tuple0;
    my_tuple tuple2;
    tuple2 = tuple0;

    my_tuple t1 = boost::make_tuple(2,"test",100.0);
    assert(t1.get<0>() == 2);
    assert(t1.get<1>() == "test");

    // 比较
    my_tuple t2 = boost::make_tuple(3,"test",200.00);
    assert(t1 < t2);

    // io
    cout << t1 << endl;
    cout << "input tuple: example: (1 test 200.0)"<<endl;
    cin >> t1;
    cout << t1 << endl;

    using namespace boost::assign;

    vector<my_tuple> v = tuple_list_of(1, "1", 1.0)(2, "2", 2.0);
    assert(v.size() == 2);

    return 0;
}

any

特殊容器,只能容纳一个元素,但这个元素可以是任意类型—— int , double, string, STL 容器或者任意自定义类型。程序可以用 any 保存任意的数据,在任何需要的时候将它取出来,这种功能与 shared_ptr<void> 有些类似,但 any 是类型安全的。 any 不是一个模板类。

不要用 any 保存原始指针,代替用 shared_ptr 智能指针。

当 any 以指针的方式传入 any_cast() 的时候,返回的指针类型与传入的 any 对象具有相同的常量。如果 any 不持有任何对象,这两个 any_cast() 不会抛出异常,而是返回一个空指针。

#include <iostream>
#include <string>
#include <boost/assign.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/assign.hpp>
#include <boost/any.hpp>
using namespace boost;
using namespace std;

// check object type in any
template< typename T>
bool can_cast(any &a){
    return typeid(T) == a.type();
}

// get value in any
template< typename T>
T& get(any &a){                     // &  不能丢失,否则无法修改 a 的值
    BOOST_ASSERT(can_cast<T>(a));
    return *any_cast<T>(&a);
}

template< typename T>
T* get_pointer(any &a){
    BOOST_ASSERT(can_cast<T>(a));
    return any_cast<T>(&a);
}

int main(int argc, const char * argv[]) {

    any a(10);
    int n = any_cast<int>(a);       // get value return a copy of a's data : 10
    // any_cast<double>(a) 	throw bad_any_cast
    assert(n == 10);

    a = string("test");
    a = vector<vector<int> >();

    a = n;
    assert(can_cast<int>(a));
    get<int>(a) = 11;                   // Will change a value this line
    *get_pointer<int>(a) = 12;

    return 0;
}

variant

与 any 类似,是可变类型,对 C++中 union 概念增强和扩展。

需要头文件:

#include <boost/variant.hpp>
using namespace boost;

例子:

#include <iostream>
#include <string>
#include <boost/assign.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/assign.hpp>
#include <boost/variant.hpp>
using namespace boost;
using namespace std;

int main(int argc, const char * argv[]) {

    typedef variant<int, double, string> var_t;
    var_t v;                                // v == 0
    assert(v.type() == typeid(int));
    assert(v.which() == 0);

    v = "test string";                      // v -> string
    cout << *get<string>(&v) << endl;       // get()

    try {
        cout << get<double>(v) << endl;
    } catch (bad_get &) {
        cout << "bad_get" << endl;
    }

    return 0;
}

variant 与 any 的区别

variant 和 any 都可以保存多类型的变量,但是他们的使用是有区别的。

  • 都可以容纳一个可变类型的元素,但是 variant 是有界类型,类型范围由用户指定,而 any 则是无界类型,可以容纳任意的类型
  • variant 可以在编译期类型检查
  • variant 提供泛型的 vistor 方式访问内部元素

multi_array

多维数组 的高效实现。 multi_array 是递归定义的。

#include <boost/multi_array.hpp>
using namespace boost;

具体用法:

#include <iostream>
#include <string>
#include <boost/assign.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/assign.hpp>
#include <boost/array.hpp>
#include <boost/multi_array.hpp>
#include <boost/multi_array/extent_gen.hpp>
using namespace boost;
using namespace std;

int main(int argc, const char * argv[]) {

    multi_array<int, 3> ma;             // 定义三维数组
    multi_array<int, 3> ma1(extents[2][3][4]);      // 定义维度 2/ 3/ 4 的三维数组

    for (int i=0 , v= 0; i < 2; ++i) {
        for (int j =0 ; j< 3; ++j) {
            for (int k  = 0; k < 4; ++k) {
                ma1[i][j][k] = v++;
            }
        }
    }

    boost::array<size_t, 3> idx = { 0,1,2};
    ma1(idx) = 10;
    cout << ma1(idx) << endl;

    boost::array<size_t, 3> arr = { 4,3,2};
    ma1.reshape(arr);

    for (int i = 0; i< ma1.shape()[0]; ++i) {
        for (int j = 0 ; j < ma1.shape()[1]; ++j) {
            for (int k = 0 ; k < ma1.shape()[2]; ++k) {
                cout << ma1[i][j][k] << ",";
            }
        }
    }

    ma1.resize(extents[2][9][9]);           // 改变多维数组的大小

    // 创建子视图
    /**
     * (0, 1, 2, 3)
     * (4, 5, 6, 7)
     * (8, 9, 10, 11)
     */

    typedef multi_array<int, 2> ma_type;
    multi_array<int, 2> mademo(extents[3][4]);
    for (int i = 0, v = 0; i< mademo.shape()[0]; ++i) {
        for (int j = 0 ; j < mademo.shape()[1]; ++j) {
            mademo[i][j] = v++;
        }
    }
    cout << endl;

    typedef ma_type::index_range range;
    indices[range(0,2)][range(0,2)];
    /**
     * (0, 1)
     * (4, 5)
     */

    BOOST_AUTO(view, mademo[indices[range(0,2)][range(0,2)] ]);
//    ma_type::array_view<2>::type view = ma[indices[range(0,2)[range(0,2)]];

    cout << view.num_dimensions() << endl;

    // 将一维数组适配成多维数组
    // 提供了 multi_array_ref 和 const_multi_array_ref
    // 适配完不能动态增长,其他和 multi_array 完全相同
    // const_multi_array_ref 功能少一些,因为它是只读的
    int arra[12];
    for (int i = 0; i < 12; ++i) {
        arra[i] = i;
    }
    multi_array_ref<int, 2> mar(arra, extents[3][4]);

    const_multi_array_ref<int, 2> cmar(arra, extents[2][6]);
    return 0;
}

property_tree

property_tree 容器比较复杂,它是一个保存了多个属性值得树形数据结构,功能强大,可以解析 xml, ini, json 和 info 四种格式的文本数据。单独拿一篇文章来讲它好了。


2016-01-10 boost , C++

星球大战:好莱坞流水线上的标准产物加上一些情怀催化一下

我果然不是星战迷,也没有星战情怀,相比同档期电影可以一看,但是我不是很喜欢这一部。 剧情6分(标准套路,困境解决困境),表演6.5(黑人演员可圈可点,女主没什么说的,机器人没有语言没有表情却异常的生动),娱乐性8分(一分光剑一分BB8)。

星战前六部,正传三部,前传三部,稍早的时候看过了,看原力觉醒之前没有认真补课。但是我知道好莱坞成熟流水线上的电影是不会要求你必须看完前六部再来看这一部电影的,每一部完整的电影都会有其完整的剧情和完整的人物描述。回到这一部原力觉醒,可能是星战迷内心的浴火被燃烧了,从第一部到今年三四十年的时间,足够让一个从青年变成中年甚至老年,伴随星战迷成长的电影总会伴随这一系列的讨论,今天早上看票房成绩已经是影史第四,超过票房第一的阿凡达已经是板上钉钉。基于这样的环境去影院看了。

剧情

就像之前说过的那样,这一部在剧情上的设定就是好莱坞标准的剧本设定,产生新的反派,然后需要正义人士组织反抗,制定计划一步一步瓦解反派,实在没有任何需要交代的内容。我甚至在观影过程中连续两次打哈欠,这实属罕见,在以前从来没有出现过,甚至在看《刺客聂隐娘》在场有人睡着的情况下我也没有困意,而这一部看到女主发觉拥有原力,Resistance组织开始制定作战计划开始就已经注定了结局。再到爱情,亲情的设定,总觉得会产生一些问题,大战刚刚结束,公主与将军的见面,再到后面父子的见面总感觉设定上有一些故意而为之。当然这些都是一些小的问题,总体来看剧情还是有一个及格的6分。至少前半部分,已经后面的一些情节,总能够让观众一边被主人公的台词表演逗笑,一边被剧情的紧张气氛而胆战心惊。

表演

再到表演,其实也并没有什么说得,反而到时那个萌萌的机器人能让观众会心一笑,有些时候表演并不需要语言和表情,一举一动都能够看出表演的出彩,或许这也是这部电影在表演上能给我留下的唯一印象,女主,黑人男主,再到哈里森福特,并没有什么特别出彩的地方。

BB 8

娱乐

观影风向标定下的娱乐性,其实一直不大明白,娱乐到底是指那一方面,如果是指话题性,新闻性,亦或是卖萌属性的话,就像上面说得那样我会一分给光剑,这一分给的是情怀,一分给BB8,那是因为这个“球”真是太可爱了,可爱到人人都会想要拥有一台。

总之这是一部让我觉得值得票价却也提不出什么优点的电影啦。感谢导演 JJ 艾布拉姆斯,感谢哈里森福特。


2016-01-09 思考感悟 , movie

guake zsh 配置小记

Guake is a drop-down terminal for the GNOME desktop which includes split terminal functionality, session save/restore, support for transparency, and many other features.

配置 zsh guake 小记

Install zsh

sudo apt-get update && sudo apt-get install zsh

Install Oh-my-zsh

wget –no-check-certificate https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O – | sh

Make zsh default shell

chsh -s $(which zsh)

Get oh-my-zsh config

From here: https://github.com/robbyrussell/oh-my-zsh

Change theme

Edit zshrc config vim ~/.zshrc

ZSH_THEME="agnoster"

Install missing fonts

Install missing fonts here: https://github.com/Lokaltog/powerline-fonts After install missing font, change terminal font to “Melon xx”.

Refresh font cache

fc-cache -f -v

Install solarized scheme to guake

According to this: https://github.com/coolwanglu/guake-colors-solarized git clone this repo and run ./set_dark solarized .

Some config of guake

  • F12 to toggle guake
  • F11 to toggle full screen

Tab management

  • Alt + T for new tab
  • Alt + W for close current tab
  • Alt + N to go to previous tab
  • Alt + P to go to next tab

Clipboard

  • Alt + C for copy text to clipboard
  • Alt + V for paste text from clipboard

Guake 自带的终端内搜索 Ctrl + Shift + F

finally

guake zsh

And here is another article about guake, I am taking some shorcut configs from this article. And if you want to build guake from source, you can take a look at thie article.

And all the source code: https://github.com/Guake/guake.

reference


2016-01-09 linux , guake , zsh , vim , agnoster , fonts , solarized

boost 学习笔记 5:文本字符串相关

字符串相关库,对应书中第五章 字符串与文本处理,大大增强了C++在文本处理上的能力。 lexical_cast 实现了字符串和数字之间的方便转换;format库提供了C++ 类似 printf() 的能力,用以格式化输出;string_algo 是一个算法库,提供了大量与字符串和文本处理相关的算法;tokenizer 库专注于文本处理领域的分词功能;xpressive 是一个灵活且功能强大的正则表达式解析库。

lexical_cast

之前单独有一篇文章讲 lexical_cast 这里不再重复。

format

format 基本集成了 printf 的格式化语法,每个 printf 格式化选项都以 % 符号开始,后面是格式规则,例如

  • %05d :输出宽度为5的整数,不足位用0填充
  • %-8.3f :输出左对齐,总宽度为8,小数位3位浮点数
  • % 10s :输出10位字符串,不足位用空格填充
  • %05X :输出宽度为5的大写16进制整数,不足位用0填充。

新增格式:

  • %|spec| : 竖线分割,区分格式化选项和普通字符
  • %N% : 标记第N个参数,相当于占位符,不带任何其他的格式化选项

主要用法

需要包含头文件

#include <boost/format.hpp>
using namespace boost;

例子:

cout << format ("%s:%d+%d=%d\n" )% "sum" % 1 % 2 % (1+2);

format fmt("(%1% + %2%) * %2% = %3%\n" );
fmt % 2 % 5 % ((2+5) * 5);
cout << fmt.str();

/**
* 程序结果
* sum:1+2=3
* (2 + 5) * 5 = 35
*/

format 还有很多高级的用法,参见文档。

string_algo

C++98 标准库中提供了字符串标准类 std::string , 它有一些基本成员函数用以查询子串,访问字符,等基本功能。

主要特点

提供全面的字符串算法库

  • 大小写无关比较
  • 修剪
  • 特定模式子串查找

主要用法

包含头文件

#include <boost/algorithm/string.hpp>
using namespace boost;

例子:

#include <iostream>
#include <vector>
#include <boost/algorithm/string.hpp>
using namespace std;
using namespace boost;

int main() {

    string str("readme.txt");
    if (ends_with(str, " txt")) {
    	cout << to_upper_copy(str) + "UPPER" << endl;              // upper case
    }

    replace_first(str, "readme ", "followme ");                       // replace
    cout << str << endl;

    vector<char> v(str.begin(), str.end());
    vector<char> v2 = to_upper_copy(erase_first_copy(v, "txt ")); // delete sub string
    for (int i = 0; i < v2.size(); ++i) {
    	cout << v2[i];
    }

    return 0;
}

string_algo 库命名遵循标准库惯例,算法名均为小写形式,并使用不同前缀或者后缀来区分不同版本,命名规则如下:

  • 前缀 i : 表示算法大小写不敏感,否则大小写敏感
  • 后缀_copy : 表示算法不变动输入,返回处理结果的拷贝,否则算法原地处理,输入即输出
  • 后缀_if : 需要判断式的谓词函数对象,否则使用默认的判断准则

string_algo 库提供算法共分为五大类:

  • 大小写转换
  • 判断式与分类
  • 修建
  • 查找与替换
  • 分割与合并

每一类算法中都会包含一系列函数。

tokenizer

主要特点

tokenizer 库是专门用于分词 token 字符串处理库,可以用简单方法把一个字符串分解成若干单词。

tokenizer 库可以容易地执行分词操作,但是它存在一些固有的缺陷。

  • 只支持单字符分割,当遇到“||”分割符时无能为力,智能自定义分词函数,或者使用 string_algo, 正则表达式等其他方法
  • 对wstring(unicode) 缺乏完善的考虑

主要用法

需包含头文件:

#include <boost/bokenizer.hpp>
using namespace boost;

例子:

#include <iostream>
#include <vector>
#include <cstring>
#include <boost/assign.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/tokenizer.hpp>
using namespace std;
using namespace boost;

template<typename T>
void print(T &tok) {
    for (BOOST_AUTO(pos, tok.begin()); pos != tok.end(); ++pos) {
    cout << *pos << "|\t";
    }
    cout << endl;
}
int main() {
    string str("Link radfe the dfafe -adfead");

    tokenizer<> tok(str);

    print(tok);

    /**
     * char_separator
     * 第一个参数 dropped_delims 分隔符集合,这个集合中的字符不会作为分词结果出现
     * 第二个参数 kept_delims 分隔符集合,其中的字符会保留在分词结果中
     * 第三个参数 empty_tokens 类似 split 算法 eCompress 参数,处理两个连续出现的分隔符。 keep_empty_tokens 表示连续出现的分隔符标识了一个空字符串。
     * 使用默认构造函数,不传入任何参数,行为等同于 char_separator(" ",标点符号字符, drop_empty_tokens) ,以空格和标点符号分词,保留标点符号,不输出空白单词。
     */
    char * s = "xxx ;; <yyy-zzz> !!!";
    char_separator<char> sep;
    tokenizer<char_separator<char>, char *> tok1(s, s + strlen(s), sep);
    print(tok1);

    char_separator<char> sep1(";-<>!", "", keep_empty_tokens);
    tok1.assign(s, s + strlen(s), sep1);
    print(tok1);

    char_separator<char> sep2(" ;-!", "<>", drop_empty_tokens);
    tok1.assign(s, s + strlen(s), sep2);
    print(tok1);

    /**
     * escaped_list_separator
     * 专门处理 CSV 格式(Comma Split Value,逗号分割值)的分词对象
     * 第一个参数 e 指定了字符串中的转义字符,默认是斜杠\
                     * 第二个参数是分隔符,默认是逗号
     * 第三个参数是引号字符,默认是"
     */

    string strcom = "id,100,name,\" mario\"";
    escaped_list_separator<char> sepcom;
    tokenizer<escaped_list_separator<char> > tokcom(strcom, sepcom);
    print(tokcom);

    /**
     * offset_separator
     * 不是基于分隔符,而是使用偏移量,在处理某些不使用分隔符,而使用固定字段宽度文本时非常有用。
     * 构造函数接受两个迭代器,也可以是数组指针begin end,指定分词用的整数偏移量序列,整个序列每个元素是分词的宽度
     *
     bool 参数 bwrapoffsets ,决定是否在偏移量用完之后继续分词
     *
     bool 参数 return_partial_last 决定在偏移量序列最后是否返回分词不足的部分
     * 最后两个参数默认值都是true
     */
    string stroffset = "2233344445566666666";
    int offsets[] = { 2, 3, 4 };
    offset_separator sepoff(offsets, offsets + 3, true, false);
    tokenizer<offset_separator> tokoff(stroffset, sepoff);
    print(tokoff);

    offset_separator sepoff2(offsets, offsets + 3, false);
    tokoff.assign(stroffset, sepoff2);
    print(tokoff);

    offset_separator sepoff3(offsets, offsets + 3, true, false);
    print(tokoff);
    return 0;
}

xpressive

正则表达式是处理文本强有力的工具,使用复杂的语法规则,能够解决文本处理领域绝大多数问题,诸如验证、匹配、查找、替换等等。xpressive 是一个先进的、灵活的、功能强大的正则表达式库,提供了对正则表达式全面的支持,而且比原正则表达式库 boost.regex 要好的是它不需要编译,速度快,同时语法又很类似。

xpressive 提供动态和静态两种方式。静态方式使用操作符重载生成编译期的表达对象,可以在编译期进行正则表达式的语法检查。动态的方式则是较传统的用法,与 boost.regex 和 Python 中的 re 模块相似,以字符串作为一个表达式对象,在运行时进行语法检查和处理。

正则表达式介绍

正则表达式定义了一套完善而复杂的语法规则,用于匹配特定模式的字符串,少量字符被用于定义特殊匹配模式语法,它们是: .^$()*+?{}[]\|。

  • 点号 (.) 匹配任意单个字符
  • ^ 尖角号 行开头
  • $ 行尾
  • () 括号,子表达式,可重复
  • * 星号,表前面元素可以重复任意多次 (n>=0)
  • + 加号,表前面元素可以重复一次或多次(n>0)
  • ? 问号,表前面的元素可以重复0次或者1次 (n=0,1)
  • {} 手动指定元素重复次数, {n}重复n次, {n,} 重复 >=n次, {n,m } 重复 n 到 m 次之间的次数, 即 n <= x <=m 次。
  • [] 定义字符集合
  • \ 转义字符
  • | 逻辑或的概念,匹配两侧的元素之一。

其他经常使用 \d 匹配数字 [0-9] , \w 匹配字母 [a-z] , \s 匹配空格等。

C++ 代码中的斜杠需要变成双斜杠,在使用正则表达式时,在语句前使用注释保存原始表达式,以方便未来的调试和维护。

  • basic_regex 是正则表达式的基本类,常用 sregexcregex 用于操作std::string ,和 C风格字符串。
  • match_results 保存正则匹配结果,常用 smatch 和 cmatch 用来支持 std::string 和 字符串。
  • sub_match 模板类类似迭代器对的对象,继承自 std::pair ,可以把它当成作一个字符串的区间。

主要用法

  • 混用两种方式,包含头文件 <boost/xpressive/xpressive.hpp>
  • 仅仅想使用静态方式,可以只包含头文件 <boost/xpressive/xpressive_static.hpp>
  • 仅仅想使用动态方式,可以只包含头文件 <boost/xpressive/xpressive_dynamic.hpp>

须有如下命名空间:

using namespace boost::xpressive;

例子:

#include <iostream>
#include <vector>
#include <cstring>
#include <boost/assign.hpp>
#include <boost/typeof/typeof.hpp>
#include <boost/tokenizer.hpp>
#include <boost/xpressive/xpressive_dynamic.hpp>
using namespace std;
using namespace boost;

int main() {
    using namespace boost::xpressive;

    string s = "Hi world, I am from Mars!";
    sregex reg = sregex::compile("(M\\w{3})");
    bool ret = regex_match(s, reg);

    // match identity card number
    // 18 number , first 6 area code, middle 8 birthday, last 4 random number possible x
    // \d{6}(1|2)\d{3}(0|1)\d[0-3]\d\d{3}(X|\d)

    // regex_search
    // regex_search 检测输入表达式中是否包含正则表达式,即存在一个匹配正则表达式的子串
    char* str = "there is a power-suit item";
    cregex creg = cregex::compile("(power)-(.{4})\\s(\\w{4})", icase);
    ret = regex_search(str, creg);

    cmatch what;
    regex_search(str , what, creg);
    for (int i = 0; i < what.size() ; ++i){
    	cout << what[i] << endl;
    }

    // 替换
    // regex_replace()
    cout << regex_replace(s , reg , "Earth") << endl; // replace Mars with Earth
    cout << regex_replace(s , reg , "$1 haha") << endl;
    cout << regex_replace(s , reg , "$1 $&") << endl;

    s = regex_replace(s , reg , "Earth");
    cout << s << endl;
    // regex_iterator<>

    string ss = "boost1, Boost2, BoOst3, etc";
    sregex ssreg = sregex::compile("boost\\d",icase);
    sregex_iterator pos(ss.begin(), ss.end(), ssreg);
    sregex_iterator end;
    while(pos != end){
        cout << (*pos)[0] << "\t";
        ++pos;
    }

    return 0;
}

2016-01-07 boost , C++

JVM parameters

如果要更加了解 JVM 启动参数,那么对 JVM 的内存结构需要有一定的了解。JVM 内存主要分为三大块:

  • Heap,又分为 Eden,From Survivor,To Survivor
  • 方法区,存储类信息,常量,静态变量
  • 栈,又分为虚拟机栈(Java Stack)和本地方法栈(Native Method Stack),用于方法执行

-Xms

初始堆大小,默认值是物理内存的 1/64 . 默认 (MinHeapFreeRatio 参数可以调整)空余堆内存小于 40% 时,JVM 就会增大堆直到 -Xmx 的最大限制。

-Xmx

最大堆大小,物理内存的 1/4(小于 1GB), 默认 (MaxHeapFreeRatio 参数可以调整)空余堆内存大于 70% 时,JVM 会减少堆直到 -Xms 的最小限制。

-XX:+HeapDumpOnOutOfMemoryError

当堆内存空间溢出时输出堆的内存快照

通常配合 -XX:HeapDumpPath 使用,输出到文件

 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/java_error_xxx.hprof

只有当发生 java.lang.OutOfMemo-ryError 时,才会 dump 到指定文件。

得到 hprof 文件后使用 memory analyzer tool(比如http://eclipse.org/mat/) 来分析。

-XX:MaxGCPauseMillis=100

设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM 会自动调整年轻代大小,以满足此值。

-XX:InitiatingHeapOccupancyPercent=25

整个堆占用量,开始 GC 。 默认值为 45,当值为 0 时,表示 “do constant GC cycles”。

-XX:+UseG1GC

使用 Garbage First(G1) Collector.

-XX:MaxJavaStackTraceDepth=1000000

JVM 人为设置了 stack trace 的限制为 1024,可以使用该参数来增加该限制。-1 值表示没有限制。

-XX:ErrorFile=/hs_err_pid%p.log

JVM 致命错误。

该文件包含如下几类关键信息:

  • 日志头文件
  • 导致 crash 的线程信息
  • 所有线程信息
  • 安全点和锁信息
  • 堆信息
  • 本地代码缓存
  • 编译事件
  • gc 相关记录
  • jvm 内存映射
  • jvm 启动参数
  • 服务器信息

2016-01-04 java , jvm , java-virtual-machine

查看系统版本和发行版信息

查看系统版本和发行版的命令有很多,这里就列一些比较常见的。

查看 Linux Kernel 版本

uname -a
Linux ev 4.4.0-66-generic #87-Ubuntu SMP Fri Mar 3 15:29:05 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

uname -r

查看发行版信息

print distribution-specific information

lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.1 LTS
Release:	16.04
Codename:	xenial

使用 inxi

inxi -S

2016-01-03 linux , command

Google Calendar 使用技巧一:添加各国节假日

用 Google 日历很多年,但却一直没有体会到 Google Calendar 的精髓,最近在使用过程中越来越得心应手,原因是结合手机的通知栏,能够非常方便的提醒日程。而 Google Calendar 在安排日程之外很棒的一点就是能够共享日历,不管是小到自己的行程安排,再到团队的时间规划安排,再大到一个国家的节假日都能非常轻松的分享出去。

早之前就已经添加了 中国的节假日日历,还有历史上的今天一些有趣的日历,这里就做个总结,在默认情况下 Google Calendar 在左边侧边会有一项 添加日历,在菜单中可以选择 “Browse calendars of interest” 然后在 Google 提供的清单中能够轻松的找到 “宗教日历”,包括 Christian,Jewish,Muslim,Orthodox 的日历,然后还有基于地区的日历,其中包括世界上很多国家的节假日日历,另外还有体育运动的日历,包括篮球、橄榄球、英式足球等各种赛事分类,甚至还有月相(Phases of the Moon) 的日历。简单的勾选即可添加到日历中。

添加节假日日历

比如说:

其实仔细观察一下就能发现这些日历的 ID,都是语言加上地区,比如说 zh-cn.china#holidayzh_cn.china#holiday 表示的就是中文显示中国的节假日,而 en.china#holiday 就表示英文显示中国的假日。

农历日历

不得不说,其实已经不用添加农历日历了,Google Calendar 已经默认自带了农历,并且和整体搭配非常美观。在设置中,“视图选项”,其中有其他日历选项,选择中文简体,那么就会添加农历日历到界面中。

其他

一个综合型日历分享网站


2016-01-03 google , google-calendar , skills , holidays

萨勒姆的女巫:The Crucible

萨勒姆的女巫

朋友邀请,去北影看了场话剧—-萨勒姆的女巫—-原本对话剧并不了解,最开始了解话剧也才是从《夏洛特烦恼》主演开心麻花,而对于话剧的概念只停留在了电影电视剧的创作中。话剧的表演确实于电影电视剧作品不同,我要刷新一下我的世界观。原来我错过了世界上很多美好的东西,而我却不知道。

剧本介绍

原剧本为,亚瑟 米勒 1953年剧本,四幕剧。该剧于1953年1月22日于百老汇的贝克剧院首次演出。之后成为了话剧经典。

小说原标题为 坩埚 “Crucible”,金属容器,可以将金属或者其他物质放入其中高温加热。剧中每个角色隐喻某种金属,接受着周围环境高温的考验。那些拥有超越死亡的道德人物,例如 约翰 普罗克托 和 瑞贝卡 寓指拒绝融化的物质。

角色

帕里斯 牧师

艾比 帕里斯侄女

蒂图巴 Tituba

约翰 普罗克托 John Proctor 率直正直的农场主,剧本的绝对男主

伊丽莎白 普罗克托 John妻子

黑尔 牧师

托马斯帕特南 想要占领别人的土地

安 帕特南 托马斯的妻子,怀孕8次,流产7次

感想

几个想留住的画面:

  • 开场时全员亮相的时候,开场时虽然有一些恐怖,但开场幽灵似得全员亮相震慑住了我。

  • 第二幕家中的对白,虽平淡,故事也很悲伤,但画面却很美好。

  • 第四幕开场伊丽莎白从台阶上出场,画面太美了。

  • 约翰和伊丽莎白的告别戏,将矛盾与个人命运推向高潮,命运的选择只在一瞬间。

很可惜的是这些画面并没有全部留下成为影像,观看的时候,我更愿意融入剧情而不愿意跳脱出来拿起相机。最后只从朋友那边盗来一些影像,不过我个人更加愿意将这些画面留在脑海。

全体亮相

伊丽莎白出场

伊丽莎白和约翰告别

剧情

在第一幕,最开始以为 帕里斯牧师 是非常正直的人物形象,而到后面几幕中形象大转变,从收受房契,到金子做的烛台,人物形象一落万丈。而同样人物形象转变的还有黑尔牧师,本来善良正直的牧师,到后来只能堕落颓废到劝说人们认罪。同样帕特南无理指控别人想要强行占他人领土地等等。人物形象的转变和刻画推动着剧情的发展。

而从第二幕开始约翰和伊丽莎白的戏开始,我才渐渐的进入剧情的设定,或者说,到第二幕的时候所有的背景故事,背景人物都才有所交代,故事才得以铺展开来。而第二幕过后剧情渐渐走向高潮,第三幕的审判,引入法官,将矛盾凸显,再到第四幕约翰伊丽莎白以及观众感到深深的绝望,将故事推向结局。

剧情中,几乎每个人的形象都刻画的比较生动。反派为各自的私心陷害别人,受到奴隶遭遇的女孩们的反抗,帕特南为夺取地产而进行的无理指控,而身为牧师的帕里斯为个人的私欲利用人们对上帝的信仰收敛钱财。(这里让我想到了那部印度电影《我个神啊》)而正面的角色,约翰虽然诚实正直,却也背叛了妻子,犯有奸淫罪,在为人上略有瑕疵,一直理性的黑尔牧师也在最后一幕变成了说服别人认罪的绝望者,而 伊丽莎白 为了拯救自己的丈夫,无奈得在法庭上作出了谎言。人人都在萨勒姆这样的炼狱中遭受折磨。

在真正的压抑和悲伤中让剧情达到高潮,然后结束整个舞台,才是真正好的剧本。

表演

对于表演真是,没办法说,就像之前一位同学和我说的一样,无论是电影还是话剧等等的剧情和表演中,在第一遍观看的时候总是让人无法集中到演员的表演上。我在看话剧的时候,第一二幕基本将注意力放到了剧情上,记忆剧情中人物的名字和相貌,整理故事中发生主角的关系,了解故事发生的地点和背景。直到第二幕过后才渐渐的融入到故事的本身。哎,真应该在事前就做一下功课的,不然观看太累太累。当然没有做事前功课也带来另外的好处,也就是剧情发展无法预料到,只有真正到黑幕降下才能知道最后的结局。有的时候心中会设定一个剧情,就像最后我会希望那些女孩会为自己的说谎和无理指控而受到惩罚,而约翰和伊丽莎白会被判为无罪,而这其实都是无力的空想,如果剧作真的做了此结局那就落入了俗套。

关于表演,第一幕中的帕里斯牧师和艾比的表演,后几幕中约翰和妻子的表演都让我印象深刻。其实个人最初的时候最喜欢帕里斯牧师的表演,但是在后来剧情的推进中帕里斯牧师渐渐地走向反面,对角色的厌恶消弱了帕里斯的表演。自第二幕起就喜欢上了约翰和伊丽莎白,就个人偏好来说,其实更加喜欢伊丽莎白平铺的对白,没有约翰激动时口喷唾沫,也没有艾比陷害别人时的高声呐喊,伊丽莎白的表演最能让我感受到自然。虽然伊丽莎白在剧中不是唯一女主,剧情扮演上分量适中,但是其诚实正直的基督徒形象让人无法忘却。

或许我们在评价一个角色表演的时候经常带入角色形象,观众永远只会喜欢剧中的正面角色,从而忽略反派角色,而事实上此剧的艾比和蒂图巴 这两个反面的角色表演同样精彩,hen 同样在第三幕出现的审判长,出现再对立面,而我却无法喜欢上他,或许是因为对长官的天然敌意,亦或许是对审判长审判的公平性的质疑,总之无法让我公正地去评价他的形象。

参考


2016-01-02

最棒的gif录屏:byzanz record

类Unix系统下最棒的gif录屏应用—- byzanz record 。可能大部分的人会在需要用到gif时,会先用录屏软件将需要截取的内容录成视频,然后用 ffmpeg 或者其他的应用将视频转成 gif 动画。而这样会产生几个问题:

  1. 麻烦,首先需要有录屏软件,二还得需要 ffmpeg 类似的转码工具
  2. 文件超大,我尝试过用 ffmpeg 转 gif,一般情况下转完之后的 gif 的文件都会比较大,即使是几秒钟的动画,大小可能也超出 gif 能够承受的大小,一般都会接近 10M。即使用 convert 优化,大小依然不乐观。

介绍

而现在要介绍的 byzanz-record 就是一个轻量级的直接录屏产生 gif 的应用。Byzanz 可以直接避免以上两个问题,将录屏这件事情变得非常容易。看Debian package上面的介绍

Byzanz is a desktop recorder and command line tool allowing you to record your current desktop or parts of it to an animated GIF, Ogg Theora, Flash or WebM. This is especially useful for publishing on the web.

Byzanz also allows recording of audio, when the output format supports it.

上面的介绍能够看到 Byzanz 的主要功能,同时说了支持的格式 GIF, Ogg Theora, Flash 和 WebM。同时使用的情况就是想要将内容发布到互联网上的时候。如果输出文件支持音频 Byzanz 也支持。

安装

有维护者将 Debian 的代码移植到 PPA,这样我们可以通过 PPA 来安装使用,打开终端,输入以下内容安装:

sudo add-apt-repository ppa:fossfreedom/byzanz
sudo apt-get update && sudo apt-get install byzanz

如果 Ubuntu 14.04 和以上版本,直接使用:

sudo apt-get install byzanz

使用

安装完毕之后可以在终端使用如下命名来熟悉使用 Byzanz:

byzanz-record --duration=10 --x=100 --y=200 --width=700 --height=400 out.gif

通过参数名字能够非常容易的知道含义,duration 就是时长,xy 就是坐标,截取画面左上角的值,widthheight 就是截取画面的寛和高,也就是画面的大小,最后就是输出文件的名字。

到现在可能有人发现 Byzanz 比较麻烦的地方了,有的时候我不知道要截取的目标的屏幕坐标和大小的时候呢,有的时候只想截取屏幕的某一个部分呢,如果只想录制一个窗口呢?

于是有人写了三个shell脚本文件:

  1. byzanz-record-window 选择一个窗口录制
  2. byzanz-record-region 选择部分窗口录制
  3. 简单GUI录制窗口

下面分别介绍这三个脚本使用,分别将这些脚本保存到本地,赋予执行权限 chmod +x filename,如果想要在终端任何地方使用,加入 $PATH 系统环境变量。

byzanz-record-window

脚本地址

下载脚本,加入 $PATH,使用例子:

  1. 运行 byzanz-record-window 30 -c output.gif
  2. 使用 alt-tab 选择想要抓取的窗口,单击
  3. 等待 10 秒钟(脚本中 $DELAY 变量设置)
  4. 听到 beep 一声,录制开始
  5. 30秒钟之后,beep 一声,录制结束

-c 参数表示byzanz将录制鼠标。

byzanz-record-region

脚本地址

需要依赖: xrectsel link. 使用 make 命令编译获取可执行的二进制。更多的配置参考项目文件。

使用详情参考上一个 section。

Gui version of byzanz-record-window

脚本地址

脚本由MHC提供。修改了以上脚本,做出了一个简单的GUI版本。

Example

长达30s钟的gif,而文件大小只有3.5M,对于Web使用来说这是非常可喜的一个大小。而对于10s左右的动画,几乎1M左右的大小,让我感到非常的震惊。

下面就是30s的gif,画质虽然损失较大,但是完全不影响观感。

byzanz record game

在我的 https://github.com/einverne/dotfiles 项目中有自动安装 byzanz 的脚本,一键安装然后使用即可。

参考


2016-01-01 record , gif

电子书

本站提供服务

最近文章

  • Glance 个人自定义 Dashboard Glance 是一个可以自行架设的个人 Dashboard 以及 RSS 订阅信息面板。
  • Fileball 一款 iOS tvOS 上的媒体播放器及文件管理器 Fileball 是一款 iOS,tvOS 上的本地文件管理器,本地音乐播放器,本地视频播放器,以及文本编辑器,Fileball 可以在 iPhone,iPad,Apple TV 上使用。Fileball 可以连接网络共享,支持 SMB,FTP,SFTP,Synology,NFS,WebDAV 等,支持 Emby,Jellyfin 等,还可以连接百度网盘,Box,Dropbox,Google Drive,OneDrive,pCloud 等,可以作为 [[Infuse]] ,[[VidHub]] 等播放器的平替,高级版本价格也比较合适。Fileball 也支持 [[IPTV]]。
  • 在日本申请入台证材料及在线提交注意事项 本文记录入台证办理的材料及提交手续,以及在使用线上提交系统的时候需要注意的点。入台证是中华民国台湾地区出入境许可证的俗称,所有进入台湾的人都需要申请此许可证。
  • 从 Buffer 消费图学习 CCPM 项目管理方法 CCPM(Critical Chain Project Management)中文叫做关键链项目管理方法,是 Eliyahu M. Goldratt 在其著作 Critical Chain 中踢出来的项目管理方法,它侧重于项目执行所需要的资源,通过识别和管理项目关键链的方法来有效的监控项目工期,以及提高项目交付率。
  • AI Shell 让 AI 在命令行下提供 Shell 命令 AI Shell 是一款在命令行下的 AI 自动补全工具,当你想要实现一个功能,敲一大段命令又记不住的时候,使用自然语言让 AI 给你生成一个可执行的命令,然后确认之后执行。