Oct 30

短代码比赛 不指定

felix021 @ 2012-10-30 17:11 [IT » 程序设计] 评论(0) , 引用(0) , 阅读(4766) | Via 本站原创
比赛的起因是这样的,@Tranch同学在SegmentFault.com提了个问题,求一个代码,可以列出字符串"qwerty"被 "." 分割的所有情况,比如 q.werty qwe.rty q.w.e.r.t.y 等等。

这个问题其实很简单,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;
}


不过当时没想写这样的代码,而是特意脑抽用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)

因为前一段时间看到 这里用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)

虽然已经很短了,但是我还是想知道,是否有更简单些的实现(一定程度上可以忽略效率和可读性),于是在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])

然后 @霄妹纸 说,实际上那个是笛卡尔积。于是用上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))

@霄妹纸 还给出了另外两个奇葩的代码,一个是 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);}

另一个是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

Don't Track Me Google 不指定

felix021 @ 2012-10-23 19:16 [IT » 网络] 评论(4) , 引用(0) , 阅读(9371) | Via 本站原创
用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。
Oct 19

vim内提权保存文件内容 不指定

felix021 @ 2012-10-19 11:16 [IT » 软件] 评论(0) , 引用(0) , 阅读(6512) | Via 本站原创
经常是用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 命令的范围)。
Sep 26

12306刷存在感@chrome 不指定

felix021 @ 2012-9-26 10:25 [IT » 网络] 评论(1) , 引用(0) , 阅读(7511) | Via 本站原创
尽管 https://dynamic.12306.cn/otsweb 的 jsessionid 有效期是一年,但是如果不几分钟发个请求的话,照样会被T出来。

所以写了这么个小代码,可以每隔一段时间自动“点”一下重新查询按钮。

=== 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>


用法:在chrome下打开订票页面,按F12,在框架内找个地方把这段代码扔进去,确定,然后在页面上点击多出来Run按钮,这样就会被个30s自动“点击”一次 [重新查询] 按钮。


Sep 23

EOSCard 1.2 不指定

felix021 @ 2012-9-23 11:20 [IT » 软件] 评论(1) , 引用(0) , 阅读(10369) | Via 本站原创
这货太TM难找了!年初找过一回,到处都没有,今天总算找到一个,这里存个档。

DOWNLOAD:
下载文件 (已下载 2080 次)
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

然后加入crontab,每隔15分钟跑一次进行更新

*/15 * * * * ~/bin/dnspod.sh
Aug 24
:set formatoptions+=m    "允许对multi_byte字符换行(否则默认只能空格或者英文标点,详见set breakat=
:set textwidth=80    "换行的长度
ggVG  "选中全文
gq "应用到选中文本

p.s. 最后两句也可以缩写成 gggpG (少一个字符)..
Aug 22

windows下的批量重命名 不指定

felix021 @ 2012-8-22 22:23 [IT » 其他] 评论(2) , 引用(0) , 阅读(7599) | Via 本站原创
可能很多同学不知道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来应急:
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
分页: 19/103 第一页 上页 14 15 16 17 18 19 20 21 22 23 下页 最后页 [ 显示模式: 摘要 | 列表 ]