About
RSS

Bit Focus


Postgresql 基本配置笔记

配置不同的数据存储目录

按照官网上说的在 initdb 阶段加 -D path / --pgdata=path 好像并没什么作用, 不知道是不是 CentOS6 的启动脚本写得有问题, 遂直接改了 /etc/init.d/postgres-X.Y 里的一行配置

PGDATA=path

initdb 就好了.

---

网络访问控制

CentOS 果然是各种奇葩, 装好之后默认的验证方式都是 ident, 实际上要改成 password 才行.

这时要启动 PG, 并且连上去执行一下

show hba_file

PG 会返回一个配置文件路径, 去这里改掉一行

# host    all             all             127.0.0.1/32            ident
# 改为下面这样
host    all             all             127.0.0.1/32            password

顺手可加上允许内网其他机器访问的规则, 比如

host    all             all             192.168.0.0/16        password

但是, 这样其实仍然无法从内网其他机器访问, 因为可能 PG 监听 (bind) 的只是 127.0.0.1

% netstat -natp | grep 5432
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN      1542/postgres

如果是这样的话, 要去改 postgresql.conf (与 hba_file 在同一个目录下), 找到 listen_addresses 这个配置 (很可能这个配置被注释掉了, 那么加上它)

listen_addresses = 'localhost,192.168.x.x' # 逗号分隔的网卡地址, 按实际情况填写

Permanent Link: /p/526 Load full text

Post tags:

 Postgres

std::function 基本实现

std::function 是在 C++11 中新增的一个用于统一包装可调用对象的模板类型. 所谓统一包装, 就是无论被包装的内容的实际类型, 只要符合相应的函数调用签名, 都可以装入一个 std::function 对象中使用. 比如

Code Snippet 0-0

#include <iostream>
#include <functional>

// 全局函数
int fn_ptr(int x, int y)
{
    return x + y;
}

// 包含 2 个 int 成员的函数对象类型
struct TwoInts {
    TwoInts(int m_, int n_)
        : m(m_)
        , n(n_)
    {}

    int operator()(int x, int y)
    {
        return x + y + m + n;
    }

    int m;
    int n;
};

int main()
{
    // 使用 std::function 类型包装全局函数指针
    std::function<int(int, int)> f(fn_ptr);
    std::cout << f(1, 2) << std::endl; // 输出 3

    // 使用 std::function 类型包装函数对象
    std::function<int(int, int)> g(TwoInts(10, 20));
    std::cout << g(1, 2) << std::endl; // 输出 33

    return 0;
}

上面的使用例子中, 两个 std::function 对象定义都在栈上. 按照 C++ 的常识, 两个对象一定有相同的尺寸, 即对它们求 sizeof 得出的值一定相等. 但用于构造这两个 function 对象的材料却有着不同的尺寸, 也就是说 function 可以 "捕获" 任何尺寸的可调用对象, 这正是其奇妙之处.

下面就来简单分析 std::function 的实现方法.

虽然 std::function 是在 C++11 中引入的, 但作为一个基本实现的分析, 本文将排除所有 C++11 的特性以避免不必要的解释. 当然, 这样会产生一个硬伤: 由于可变参数模板特性也是 C++11 中引入的特性, 本文的实现中将不支持任意多个模板类型参数, 而是使用返回值类型加上 2 个参数的类型共计 3 个类型作为模板的类型参数列表. 亦即, 在 C++11 中, 下面的用法是可能的

std::function<double()> f;         // 只有返回值类型 <double> 的特化
std::function<int(std::string)> g; // 有返回值类型和 1 个参数类型 <int, std::string> 的特化
std::function<void(int, float)> h; // 有返回值类型和 2 个参数类型 <void, int, float> 的特化
// 可以扩展为任意多个参数类型的特化, 这是 C++11 的新特性

而本文中要实现的只包含下面这样的形式

Code Snippet 0-1

// 默认特化没有实现
template <typename T>
class function;

// 实现有返回值类型和 2 个参数类型的偏特化
template <typename Ret, typename Arg0, typename Arg1>
class function<Ret(Arg0, Arg1)> {
    // ...
};

语法上, 类似上面的 function<int(int, int)>, class function<Ret(Arg0, Arg1)> 等类似函数签名的模板特化形式并不常见, 虽然它是 C++11 之前就一直存在的语法. 抛开语法层面的部分, function 实现中最重要的就是如何在内部维护不同类型不同尺寸的可调用对象.

Permanent Link: /p/525 Load full text

Post tags:

 STL
 C++11
 C++

Redis Cluster 简单配置与动态扩容

    Redis 3.0 就要自带集群功能了, 去看了一下这里还有官方教程之后, 发现似乎必须用命令行来搞着, 而且官方提供的 redis-trib.rb 要求至少 3 个节点才能建立一个集群, 这规格是向党支部看齐么...
    至少 3 个节点这个还是略坑, 而且不能自动添加节点 (难道要我启动个 py 的 subprocess 去掉 ruby?), 于是去看看源代码, 惊讶地发现, 原来限制 3 个节点起步的是 ruby 脚本, 而且调集群加节点平衡负载其实都可以用 redis 命令来完成. 好吧, 那我自己来连 socket 搞总行了吧.
    结果一番折腾还真的可行的样子, 于是有了这篇文章和一个简单的工具. 那么首先说说怎么用 redis-cli 来做这些事情.

    如何在 redis-cli 手动启动集群呢, 请随意连上一个空的支持集群模式的节点, 然后执行
cluster addslots 0 1 2 ... 16383
    千万不要误会了, 中间那个 ... 可是要实打实地从头写到尾的哦. 所以如果可以的话, 手动写个脚本来干这事情吧.
    不过也可以略过这些步骤, 反正下面看看例子就行, 最后会给出一个 Python 工具来做这些.
    接下来的例子中, 假定已经开好了一个集群, 共有 3 个 master 节点. 要在控制台检视这些节点, 请用 redis-cli 随意连上其中一个, 并执行
cluster nodes
    输出
e7f4fcc0dd003fc107333a4132a471ad306d5513 127.0.0.1:8001 master - 0 1414033928009 3 connected 0-2729 8192-10921
bd239f7dbeaba9541586a708484cdce0ca99aba5 127.0.0.1:8000 master - 0 1414033929011 2 connected 2730-8191
787e06e9d96e6a9a3d02c7f3ec14e243882293e9 127.0.0.1:7999 myself,master - 0 0 1 connected 10922-16383
以上每一行是一个节点信息, 按空格分隔的域依次表示
  • 节点 ID
  • 节点地址
  • 节点角色 (master / slave), 如果是当前节点, 还会有个 myself
  • 对于 slave 而言, 其 master 节点的 ID
  • 最后一次 ping 时间戳
  • 最后一次 pong 时间戳
  • 节点顺序号
  • 节点连接状态
  • 之后的所有 : 节点所配给的槽位, 如果槽位连续, 就以 BEGIN-END 表示, 不连续的由空格隔开
    如果要向集群新增一个节点, 需要用 redis-cli 连上这个新节点, 调用一次 cluster meet 命令. 如
cluster meet 127.0.0.1 7999
后面参数是已经在集群中的节点中任意一个节点的地址及端口. 然后再来一次

Permanent Link: /p/524 Load full text

Post tags:

 Python
 Redis
 集群

如何弄一个在不同站点做不同事情的 Chromium 扩展

    先解释一下为什么有这个需求.
    国内似乎有不少所谓的说好听叫资源聚合网站说直白叫盗文章的网站, 虽然鄙博客文章质量很一般, 但也至少被三个不同的网站全文抓取了 (http://outofmemory.cn/ http://www.taocms.org/ http://www.tuicool.com/). 其实流量点击量什么的都不是个事, 我也没打算靠写博客赚钱, 问题是这些网站长得都太残了. (tuicool 还好一点, outofmemory 代码都没用等宽字体你那网站能看! 简直白白浪费这么好个域名) 于是就有了这么个需求: 当访问到这些网站时自动跳转到原博客页面.
    当然了各位读者不必搞这么过河拆桥的需求, 大可写个插件去展开豆瓣页面上的那些短网址什么的.

    简单看一下 Chromium 扩展的结构, 无非就是一个配置文件 (manifest.json) 加上一些 JS 文件, 有必要的话再加上一些 HTML 文件. 这里就说说最简单的, 进入一个网站在页面加载完毕之后执行一个指定 JS 文件中的代码. 那么配置文件要这么写
{
  "name": "ExtensionName",
  "version": "0.1.0",
  "description": "ext descr",
  "browser_action": {
    "default_title": "Extension Title"
  },
  "content_scripts": [
    {
      "matches": ["http://ju.outofmemory.cn/entry/*"],
      "js": ["outofmemory.js"]
    }
  ],
  "manifest_version": 2
}
    以上 JSON 中, content_scripts 部分是个数组, 其中每个元素有至少两个属性, matches 表示在 URL 满足什么条件时加载脚本, 而 js 则是加载那些脚本; 如果扩展要用到 jQuery 之类的库, 可以加到此数组最前面.
    也就是说, 上面这个例子会在所有 URL 开头为 http://ju.outofmemory.cn/entry/ 的网页上, 调用 outofmemory.js 这个文件. 而这个文件的内容很简单
-function() {
    console.log('Hello world!');
    // window.location = document.getElementsByClassName('copyright')[0].getElementsByTagName('a')[1].href;
}()
    想了一下我还是把代码藏着点, 仍然用业界标准的 Hello world 来开场好了.
    新建一个目录, 把这两个文件保存在此目录下.
    点 Chromium 浏览器菜单 -> 工具 (Tools) -> 扩展程序 (Extensions), 勾选开发者模式 (Developer mode) 下面会刷出来 3 个按钮, 最左边就是加载野生扩展, 在对话框中选中刚才新建的目录, 这样扩展就上线了. 然后挑个页面进去看看控制台吧, 比如 http://ju.outofmemory.cn/entry/81081
    到此代码能执行了, 后面就没什么需要继续说的了, 剩下就是自己抓 DOM 看该怎么搞怎么搞吧.

    附项目地址

Permanent Link: /p/523 Load full text

Post tags:

 Javascript
 Chromium 扩展

麻将听牌算法 [下篇]

上篇中分析了听牌可能有关字牌的情形, 具体包括字牌中有一个单张, 而剩下的数牌全能构成面子的单骑醒, 或者字牌中有个对子, 而剩下某数牌含有一个对子的双碰型或一个搭子的边/嵌张听牌. 这篇要讨论字牌全是刻子时的类似情况. 之所以说类似是由于此时数牌只可能有以下两种情况
  • 某一色数牌的牌总数模 3 余 1, 其它两个色都能恰好构成面子
  • 某两色数牌的牌总数摸 3 余 2, 剩下一色能恰好构成面子
体现成代码就是, 需要解决以下两个函数
def _waits_4groups(tiles):
    # 前略
    # 在前面情况不满足时, 调用如下实现
    return (_detect_numeric_suit_with_one_more(tiles) +
            _detect_2_numeric_suits_with_2_more(tiles))

# 找一个花色, 它的数量模 3 余 1
def _detect_numeric_suit_with_one_more(tiles):
    pass

# 找两个花色, 它们各自的牌的数量模 3 都余 2
def _detect_2_numeric_suits_with_2_more(tiles):
    pass
在上一篇代码的支援下, 后一个函数的实现相对容易一些, 如下

Permanent Link: /p/522 Load full text

Post tags:

 Algorithm
 麻将
 Python

麻将听牌算法 [上篇]

    作为一个人类经常在打清一色的时候望着手牌不知道听牌没不知道听了哪几张也不知道切哪一张会让听牌数量最大化是一件不愉快的事情, 除了九莲宝灯之类的定式役给背下来好像没别的有效方法. 或者, 写个程序来搞吧.
    首先是数据结构, 这里用如下类来描述

Permanent Link: /p/521 Load full text

Post tags:

 麻将
 Algorithm
 Python

数学证明的边界扩张

    最近看着证明与反驳这本书, 虽然不是什么数学著作, 讨论的也不是改变世界的重要定理, 不过围绕着多面体面棱角个数关系的那个欧拉定理讲得风生水起, 还是挺有意思的.
    书里提到数学研究的一个策略, 对于一个已经证明的定理, 通过想方设法扩张其证明步骤中的条件「边界」, 也许可以把定理从一个特殊形式扩展到普遍形式. 就像写程序库一样, 总希望写出来的东西能尽可能满足各种不同参数下的需求, 虽然在软件行业这么干往往会把自己玩死...
    比如看看费马平方和定理第四步的结论 (请一定看完前四步证明, 后文的内容均是修改此证明)
  • 对于互素的任意整数 a 和 b, a2 + b2 的每一个因子也都能表示为两个整数的平方和的形式
    这一步虽然内容上有不少平方和, 不过还不是平方和定理实际内容, 只是一步中间引理. 然而即使如此证明看起来也挺费神的.
    要扩展这个定理的话, 可以从多个不同角度出发, 对于两个整数平方和成立, 对于三个整数平方和是否成立; 或者, 本文将采取的方式如下
  • 给定正整数 v, u, 对于互素的任意整数 a 和 b, v * a2 + u * b2 的每一个因子也都能表示为 v * p2 + u * q2 的形式
    然后试着往原来的证明过程中套, 看看会发生什么.

    第一步用到了名字巨长的恒等式, 这个等式说两个平方和的乘积还是一个平方和, 在代数学中有个术语叫封闭性. 比如整数集里面任取两个元素出来进行加减乘运算, 结果还是整数 (除就不一定了), 那么加减乘这三种运算 (三个二元函数) 对于整数集就是封闭的. 类似的, 上述恒等式说明在所有能表示成两个整数平方和的数的集合内, 乘法是封闭的.
    那么这个结论能否运用到形如 v * a2 + u * b2 的整数呢? 很快就能验证
(v * a2 + u * b2) * (v * c2 + u * d2)
  = v2 * (ac)2 + vu * ((ad)2 + (bc)2) + u2 * (bd)2
  = v2 * (ac)2 + u2 * (bd)2 + 2 * uv * abcd
    + vu * ((ad)2 + (bc)2- 2 * uv * abcd
  = (vac + ubd)2 + vu(ad - bc)2
    并不是 (v * p2 + u * q2) 而是 (p2 + vu * q2) 的形式. 真糟糕, 第一步封闭性就阵亡了. 那么今天的内容就到这里, 谢谢大家观看, 我们下期节目再见...
    噢等等, 我觉得这个扩张还可以抢救一下, 只要作出一点小牺牲, 把内容收缩一点就好: 令 v, u 之一等于 1, 也就是说变成比如

Permanent Link: /p/520 Load full text

Post tags:

 数学

VerbalExpressions 与状态机词法分析器

VerbalExpressions

    说到字符串检索分析替换修改自然会想到正则表达式, 不过这东西实在是一个只写语言. 更改系统中一个一般复杂的正则表达式, 传统的读懂代码然后替换一条语句或者加上一个分支或者参数的模式不管用, 而是直接重写, 就像清理一个塞满的垃圾桶, 方法不是把垃圾一点点挖出来, 而是整个倒掉再铺上新的垃圾袋; 正则表达式有时太复杂了, 一条语句一个调用就顶过一打的循环和分支.
    人们总会想到一些更节省脑细胞的方式来对付字符串, 让机器理解人类的咒语, 于是发明了 VerbalExpressions.
    下面是一个 JS 的例子
var tester = VerEx()
            .startOfLine()
            .then("http")
            .maybe("s")
            .then("://")
            .maybe("www.")
            .anythingBut(" ")
            .endOfLine();
    上面这一串等价于 /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/ 这么个正则表达式, 不过书写起来显得科学多了; 如果需要更改逻辑, 也很容易下手到底是什么地方需要增加或者减少一点什么.

基于自动机的词法分析器

    这个轮子很有启发性, 于是乎想到以类似的方式构造个词法解析器. 接口上的愿景是类似
var t = Tokenizer();
t.simpleSymbols('+-*/=', 'operator')
 // ...
 .ignore(' ')
 .ignore('\t')
 .ignore('\r')
 // ...
 // 上面都是单独的一个字符, 接下来是循环的模式
 .loop(DIGITS)
 .accept('integer') // 以 0-9 循环的模式, 接受为整数类型

 .startWith(LETTERS)
 .loop(LETTERS + DIGITS)
 .accept('identifier') // 以字母开头, 数字和字母循环的模式, 接受为标识符

 // 接下来是保留字
 .fixed('if')
 .fixed('for')
 // 以及一些超过 1 字符的操作符
 .fixed('==', 'operator')
 .fixed('<=', 'operator')
 // ...
;
var inputString = 'for (i = 0; i < 10; i = i + 1) { if (i % 3 == 0) { print(i); } }';
var tokenArray = t.tokenize(inputString);
console.log(tokenArray);
    看起来应该是这么回事. 以这种方式构造出来的东西应该是一个状态机而不是一大波正则表达式形成的集群. 因此首先得构造一个状态数据结构. 作为一个演示就不弄太复杂了, 它看起来类似

Permanent Link: /p/519 Load full text

Post tags:

 Javascript
 VerbalExpressions
 Compiler Construction

Page 0 1 2 3 4 5 6 7 8 9 10 11 12


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site