Oct
30
比赛的起因是这样的,@Tranch同学在SegmentFault.com提了个问题,求一个代码,可以列出字符串"qwerty"被 "." 分割的所有情况,比如 q.werty qwe.rty q.w.e.r.t.y 等等。
这个问题其实很简单,qwerty中间最多可以塞5个". ",每个地方用1表示塞,0表示不塞,也就是正好循环 2^5 次就行了(对于全0的情况不做特别要求,可有可无),实现起来也非常容易,这里是写这篇文章时补充的一个C语言实现:
不过当时没想写这样的代码,而是特意脑抽用python写了个递归的版本:
因为前一段时间看到 这里用21行python代码实现了一个拼写检查器,于是一时兴起,简化成了这个等价但是更难读的版本:
虽然已经很短了,但是我还是想知道,是否有更简单些的实现(一定程度上可以忽略效率和可读性),于是在MSTC的群里发了这个问题,简单起见,把字符串改成了"abcde",问问有没有更短的代码来给出各种组合。
然后 @杭神 扔了个代码出来,被喷“能不能用人话”。这段代码看起来是有些费解,主要思路是,生成 ['****', '***.', '**..', '*...', '....'] 的各种排列,然后用zip('abcd', p)交错组合起来(再删掉'*'):
然后 @霄妹纸 说,实际上那个是笛卡尔积。于是用上itertools.product,再改善下语法,可以写成这样,看起来就清晰多了:
@霄妹纸 还给出了另外两个奇葩的代码,一个是 C 的,充分利用和宏、main函数的参数和递归:
另一个是ruby的:
由于不懂ruby语法,所以这个代码我也只能勉强看看,不过思路上跟上面的python代码是一样的,使用笛卡尔积生成组合序列,然后再与'abcde'交错组合。
结果是,ruby赢了(58个字节),python紧随其后(80字节,不包括import),C语言则意外地以106个字节的代码实现了这个目标。
这个问题从实践的角度上来说没有太大意义,不过可以对比下,不同的语言(C/Ruby/Python)、不同的编程范型(过程式/函数式)的表达方式,一窥函数式编程的魅力~
这个问题其实很简单,qwerty中间最多可以塞5个". ",每个地方用1表示塞,0表示不塞,也就是正好循环 2^5 次就行了(对于全0的情况不做特别要求,可有可无),实现起来也非常容易,这里是写这篇文章时补充的一个C语言实现:
#include <stdio.h>
int main()
{
char str[] = "qwerty";
int i, j;
for (i = 0; i < (1<<5); i++)
{
for (j = 0; j < 5; j++)
{
putchar(str[j]);
if (((i >> j) & 1) == 1)
putchar('.');
}
printf("y\n");
}
return 0;
}
int main()
{
char str[] = "qwerty";
int i, j;
for (i = 0; i < (1<<5); i++)
{
for (j = 0; j < 5; j++)
{
putchar(str[j]);
if (((i >> j) & 1) == 1)
putchar('.');
}
printf("y\n");
}
return 0;
}
不过当时没想写这样的代码,而是特意脑抽用python写了个递归的版本:
def add_dots_l(str):
ret = []
for i in range(1, len(str)):
left = str[:i]
right = str[i:]
ret.append(left + '.' + right)
ret += [j + '.' + right for j in add_dots_l(left)]
ret += [left + '.' + j for j in add_dots_l(right)]
return set(ret)
ret = []
for i in range(1, len(str)):
left = str[:i]
right = str[i:]
ret.append(left + '.' + right)
ret += [j + '.' + right for j in add_dots_l(left)]
ret += [left + '.' + j for j in add_dots_l(right)]
return set(ret)
因为前一段时间看到 这里用21行python代码实现了一个拼写检查器,于是一时兴起,简化成了这个等价但是更难读的版本:
def add_dots(s):
r = [s[:i] + '.' + s[i:] for i in range(1, len(s))]
r += [j + '.' + s[i:] for i in range(1, len(s)) for j in add_dots(s[:i])]
r += [s[:i] + '.' + j for i in range(1, len(s)) for j in add_dots(s[i:])]
return set(r)
r = [s[:i] + '.' + s[i:] for i in range(1, len(s))]
r += [j + '.' + s[i:] for i in range(1, len(s)) for j in add_dots(s[:i])]
r += [s[:i] + '.' + j for i in range(1, len(s)) for j in add_dots(s[i:])]
return set(r)
虽然已经很短了,但是我还是想知道,是否有更简单些的实现(一定程度上可以忽略效率和可读性),于是在MSTC的群里发了这个问题,简单起见,把字符串改成了"abcde",问问有没有更短的代码来给出各种组合。
然后 @杭神 扔了个代码出来,被喷“能不能用人话”。这段代码看起来是有些费解,主要思路是,生成 ['****', '***.', '**..', '*...', '....'] 的各种排列,然后用zip('abcd', p)交错组合起来(再删掉'*'):
from itertools import permutations as p #itertools.permutations是python2.6引入的
map(lambda p: ''.join(j for i in zip('abcd', p) for j in i).replace('*', '') + 'e', [''.join(y) for x in map(lambda i: set(p('*' * (4-i) + '.' * i)), range(5)) for y in x])
map(lambda p: ''.join(j for i in zip('abcd', p) for j in i).replace('*', '') + 'e', [''.join(y) for x in map(lambda i: set(p('*' * (4-i) + '.' * i)), range(5)) for y in x])
然后 @霄妹纸 说,实际上那个是笛卡尔积。于是用上itertools.product,再改善下语法,可以写成这样,看起来就清晰多了:
from itertools import product #itertools.product也是2.6引入的
map(lambda p: ''.join(i + j for i, j in zip('abcd', p)) + 'e', product(['.', ''], repeat = 4))
map(lambda p: ''.join(i + j for i, j in zip('abcd', p)) + 'e', product(['.', ''], repeat = 4))
@霄妹纸 还给出了另外两个奇葩的代码,一个是 C 的,充分利用和宏、main函数的参数和递归:
#define z(a,b) printf(#a"%s",(x>>b)&1?".":""),
main(x){z(a,3)z(b,2)z(c,1)z(d,0)puts("e");16-x&&main(x+1);}
main(x){z(a,3)z(b,2)z(c,1)z(d,0)puts("e");16-x&&main(x+1);}
另一个是ruby的:
p ("b".."e").inject(["a"]){|a,q|a.product [q,"."+q]}.map &:join
如果是ruby 1.9+的话,还可以再少几个字符:p (?b..?e).inject([?a]){|a,q|a.product [q,?.+q]}.map&:join
由于不懂ruby语法,所以这个代码我也只能勉强看看,不过思路上跟上面的python代码是一样的,使用笛卡尔积生成组合序列,然后再与'abcde'交错组合。
结果是,ruby赢了(58个字节),python紧随其后(80字节,不包括import),C语言则意外地以106个字节的代码实现了这个目标。
这个问题从实践的角度上来说没有太大意义,不过可以对比下,不同的语言(C/Ruby/Python)、不同的编程范型(过程式/函数式)的表达方式,一窥函数式编程的魅力~
Oct
23
用Google的第一大烦恼是随时被墙。
番羽土啬吧。
用Google的第二大烦恼是,点击的链接总要过一道Google的统计。比如搜索test,第一条是www.test.com,但是点击的时候,打开的页面是
http://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&frm=1&source=web&cd=1&ved=0CCoQFjAA&url=http%3A%2F%2Fwww.test.com%2F&ei=LHuGUMSUDKf-iAeg7YCAAg&usg=AFQjCNH21KLjC0CBkjon2DwD_CZ0HApLMw
经常发生的事情是,一直卡在这个链接上,不管是否番羽土啬,反正总是跳不过去;而大部分情况下,目标网页不需要番羽土啬也是可以顺利打开的。
至于Google呢,他才不管你是不是总卡死在这个链接,反正他们要的是你这次点击的数据。
这个问题,可以借助一个叫"Don't Track Me Google"的grease monkey脚本来实现:
http://userscripts.org/scripts/show/121923
对于Firefox,需要安装Grease Monkey插件,而Chrome用户则方便了,直接可以作为插件使用。
页面右上方有个不起眼的 Install 按钮,点一下,会下载一个 xxxx.user.js ,旧版本的chrome就直接当插件安装上去了,新版本的会提示你,不能这么安装非官方来源的插件。解决方法是:打开扩展管理页面,把这个 js 文件拖进去,会问你是否要安装,点击“添加”,done。
番羽土啬吧。
用Google的第二大烦恼是,点击的链接总要过一道Google的统计。比如搜索test,第一条是www.test.com,但是点击的时候,打开的页面是
http://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&frm=1&source=web&cd=1&ved=0CCoQFjAA&url=http%3A%2F%2Fwww.test.com%2F&ei=LHuGUMSUDKf-iAeg7YCAAg&usg=AFQjCNH21KLjC0CBkjon2DwD_CZ0HApLMw
经常发生的事情是,一直卡在这个链接上,不管是否番羽土啬,反正总是跳不过去;而大部分情况下,目标网页不需要番羽土啬也是可以顺利打开的。
至于Google呢,他才不管你是不是总卡死在这个链接,反正他们要的是你这次点击的数据。
这个问题,可以借助一个叫"Don't Track Me Google"的grease monkey脚本来实现:
http://userscripts.org/scripts/show/121923
对于Firefox,需要安装Grease Monkey插件,而Chrome用户则方便了,直接可以作为插件使用。
页面右上方有个不起眼的 Install 按钮,点一下,会下载一个 xxxx.user.js ,旧版本的chrome就直接当插件安装上去了,新版本的会提示你,不能这么安装非官方来源的插件。解决方法是:打开扩展管理页面,把这个 js 文件拖进去,会问你是否要安装,点击“添加”,done。
Oct
19
经常是用vim打开了某个/etc下的文件,使用 :w 保存的时候提示 "E45: 'readonly' option is set (add ! to override)",而 :w! 则提示 "E212: Can't open file for writing"。通常的解决方案是 :q! 然后 sudo !! 再次编辑,然后保存。
虽然挺麻烦,但是一直也就将就用了。今天有人在SegmentFault.com提出了这个问题,于是仔细考虑了下,的确还是有办法实现的:
:%!sudo bash -c "cat > '%'" #为了防止文件名中的空格之类特殊字符,最好在后一个%前后加上引号
:w !sudo tee % #另一个回答给出的这个方法更简洁,出自 http://vim.wikia.com/wiki/Su-write
其实最初的想法是 :%!sudo cat > % ,但是由于权限的问题(vim打开的shell没法清空当前文件),所以还是得请shell进来掺和一脚:先用sudo给shell提权,然后shell再执行cat将stdin的内容输出到%。
p.s. 顺便给不了解 :%! 这个含义的同学普及一下, :!xxx 是执行 xxx 命令, :%!xxx 则是把当前缓冲区的所有内容当成 stdin 去执行 xxx 命令,然后将xxx的stdout再替换当前缓冲区的内容。比较常见的用法是 :%!sort 给文件所有的行排序,然后 :%!uniq 去重。另外%可以用范围来代替(同 :s 命令的范围)。
虽然挺麻烦,但是一直也就将就用了。今天有人在SegmentFault.com提出了这个问题,于是仔细考虑了下,的确还是有办法实现的:
引用
:w !sudo tee % #另一个回答给出的这个方法更简洁,出自 http://vim.wikia.com/wiki/Su-write
其实最初的想法是 :%!sudo cat > % ,但是由于权限的问题(vim打开的shell没法清空当前文件),所以还是得请shell进来掺和一脚:先用sudo给shell提权,然后shell再执行cat将stdin的内容输出到%。
p.s. 顺便给不了解 :%! 这个含义的同学普及一下, :!xxx 是执行 xxx 命令, :%!xxx 则是把当前缓冲区的所有内容当成 stdin 去执行 xxx 命令,然后将xxx的stdout再替换当前缓冲区的内容。比较常见的用法是 :%!sort 给文件所有的行排序,然后 :%!uniq 去重。另外%可以用范围来代替(同 :s 命令的范围)。
Sep
26
尽管 https://dynamic.12306.cn/otsweb 的 jsessionid 有效期是一年,但是如果不几分钟发个请求的话,照样会被T出来。
所以写了这么个小代码,可以每隔一段时间自动“点”一下重新查询按钮。
=== update @ 16:40 ===
其实简单一点,按下F12,在console里面执行这个就行了...
=== OLD ===
所以写了这么个小代码,可以每隔一段时间自动“点”一下重新查询按钮。
用法:在chrome下打开订票页面,按F12,在框架内找个地方把这段代码扔进去,确定,然后在页面上点击多出来Run按钮,这样就会被个30s自动“点击”一次 [重新查询] 按钮。
所以写了这么个小代码,可以每隔一段时间自动“点”一下重新查询按钮。
=== update @ 16:40 ===
其实简单一点,按下F12,在console里面执行这个就行了...
var f=document.getElementById('main'); var btn=f.contentDocument.getElementById('submitQuery'); setInterval("btn.click()", 30000);
=== OLD ===
所以写了这么个小代码,可以每隔一段时间自动“点”一下重新查询按钮。
<div>
<textarea id="code">
var x=setInterval("document.getElementById('submitQuery').click();", 30000);alert(x);
</textarea>
<input type="button" value="run" onclick="javascript:try{eval(document.getElementById('code').value);}catch(e){alert(e);}"/>
</div>
<textarea id="code">
var x=setInterval("document.getElementById('submitQuery').click();", 30000);alert(x);
</textarea>
<input type="button" value="run" onclick="javascript:try{eval(document.getElementById('code').value);}catch(e){alert(e);}"/>
</div>
用法:在chrome下打开订票页面,按F12,在框架内找个地方把这段代码扔进去,确定,然后在页面上点击多出来Run按钮,这样就会被个30s自动“点击”一次 [重新查询] 按钮。
Sep
23
Sep
3
#!/bin/bash
API=https://dnsapi.cn/Record.Ddns
IP_FILE=/tmp/dnspod_ip
function get_old_ip()
{
ip=
if [ -e "$IP_FILE" ]; then
ip=`cat $IP_FILE`
fi
echo $ip
}
function save_ip()
{
echo -n $1 > $IP_FILE
}
function get_new_ip()
{
echo `nc ns1.dnspod.net 6666`
}
email=帐号邮箱
password=帐号密码 #dnspod就不能搞成个secret_key么!明文密码让人很不舒服啊。
domain_id=XXXXX #使用Domain.List API获取
record_id=YYYYY #使用Record.List API获取
sub_domain="zzz" #DDNS的二级域名
new_ip=`get_new_ip`
old_ip=`get_old_ip`
if [ "$new_ip" != "$old_ip" ];
then
curl $API -d "format=json&login_email=$email&login_password=$password&domain_id=$domain_id&record_id=$record_id&sub_domain=$sub_domain&record_line=默认"
save_ip $new_ip
fi
API=https://dnsapi.cn/Record.Ddns
IP_FILE=/tmp/dnspod_ip
function get_old_ip()
{
ip=
if [ -e "$IP_FILE" ]; then
ip=`cat $IP_FILE`
fi
echo $ip
}
function save_ip()
{
echo -n $1 > $IP_FILE
}
function get_new_ip()
{
echo `nc ns1.dnspod.net 6666`
}
email=帐号邮箱
password=帐号密码 #dnspod就不能搞成个secret_key么!明文密码让人很不舒服啊。
domain_id=XXXXX #使用Domain.List API获取
record_id=YYYYY #使用Record.List API获取
sub_domain="zzz" #DDNS的二级域名
new_ip=`get_new_ip`
old_ip=`get_old_ip`
if [ "$new_ip" != "$old_ip" ];
then
curl $API -d "format=json&login_email=$email&login_password=$password&domain_id=$domain_id&record_id=$record_id&sub_domain=$sub_domain&record_line=默认"
save_ip $new_ip
fi
然后加入crontab,每隔15分钟跑一次进行更新
*/15 * * * * ~/bin/dnspod.sh
Aug
24
:set formatoptions+=m "允许对multi_byte字符换行(否则默认只能空格或者英文标点,详见set breakat=)
:set textwidth=80 "换行的长度
ggVG "选中全文
gq "应用到选中文本
p.s. 最后两句也可以缩写成 gggpG (少一个字符)..
:set textwidth=80 "换行的长度
ggVG "选中全文
gq "应用到选中文本
p.s. 最后两句也可以缩写成 gggpG (少一个字符)..
Aug
22
可能很多同学不知道Windows是自带这个功能的。至少从WinXP开始就有。操作要领:
0. 批量重命名的文件应该在一个文件夹内。
1. 选择所有需要被重命名的文件。全选(CTRL+A),或拖动/用shift选择选择一个区间,或按住CTRL一个一个点,都可以。
2. 使用鼠标右键选择你希望被重命名的第一个文件,输入新的文件名。注意,新的文件名如果不带编号(如a.txt),那么会按顺序改成 a.txt, a (1).txt ... a (5).txt,如果带编号(如 b (3).txt,注意编号只能用圆括号包起来),则编号会按顺序增加:b (3).txt, b (4).txt ... b (11).txt。
3. 回车,OVER。
功能很贴心,但是半残,有时候不满足要求,在没有安装额外的语言的情况下,可以用类似如下的vbs来应急:
0. 批量重命名的文件应该在一个文件夹内。
1. 选择所有需要被重命名的文件。全选(CTRL+A),或拖动/用shift选择选择一个区间,或按住CTRL一个一个点,都可以。
2. 使用鼠标右键选择你希望被重命名的第一个文件,输入新的文件名。注意,新的文件名如果不带编号(如a.txt),那么会按顺序改成 a.txt, a (1).txt ... a (5).txt,如果带编号(如 b (3).txt,注意编号只能用圆括号包起来),则编号会按顺序增加:b (3).txt, b (4).txt ... b (11).txt。
3. 回车,OVER。
功能很贴心,但是半残,有时候不满足要求,在没有安装额外的语言的情况下,可以用类似如下的vbs来应急:
Set a= WScript.createObject("WScript.Shell")
WScript.sleep 1000
For i = 1 to 23
j = i
If i < 10 Then
j = "0" & i
End If
a.sendkeys "{DOWN}{F2}" '向下,F2(重命名)
a.sendkeys "TBBTs02e" & j '文件名, win7下就不用再 & ".rmvb" 了,但是xp需要
a.sendkeys "~" '回车
WScript.sleep 200
Next
WScript.sleep 1000
For i = 1 to 23
j = i
If i < 10 Then
j = "0" & i
End If
a.sendkeys "{DOWN}{F2}" '向下,F2(重命名)
a.sendkeys "TBBTs02e" & j '文件名, win7下就不用再 & ".rmvb" 了,但是xp需要
a.sendkeys "~" '回车
WScript.sleep 200
Next