略记clojure中Java交互的两个坑

好久没写东西了…从春节回来之后就没有西闲下来过,搞定了一些重要的事情~^_^。现在项目进入紧张的开发过程中,自己也开始尝试再非核心的项目中引入clojure。记一下最近遇到的两个坑吧,关于clojure的java interop的。

repeat与java对象

我其实需要一组java对象的seq,把他们new出来就好。于是我写了这样的代码…

(repeat (SomeClass. ))

后来代码行为很奇特,于是…于是…我发现我不是在操作一组java对象,而是同一个对象。后来一想,同一个对象才是符合clojure的行为的。因为clojure自身的对象都是不可变的,那么repeat返回同一个对象是理所当然的了!来看看repeat的源代码:

(defn repeat
  "Returns a lazy (infinite!, or length n if supplied) sequence of xs."
  {:added "1.0"
   :static true}
  ([x] (lazy-seq (cons x (repeat x))))
  ([n x] (take n (repeat x))))

已经很明显了,果断返回同一个对象啊…看来是自己想当然了,经过clojure-cn邮件组的同学的提示,正确的写法应该是:

(repeatedly cnt #(SomeClass. ))

java异常

我只是想说,java interop的时候,一定别忘了catch 异常,一定记得把java调用的代码用try包起来,不然线程推出了都不知道是为啥!

 

p.s. 原文地址:http://chunyemen.org/archives/852
p.s.s. 欢迎访问纯爷们的小生活

在Mac OSX上通过SSH代理实现github访问

前提,你需要一个ssh代理。

创建如下两个文件,并加上可执行权限。
~/.socks5proxyssh:

#!/bin/sh
ssh -o ProxyCommand="/Users/xiafei/.socks5proxywrapper %h %p" "$@"

~/.socks5proxywrapper

#!/bin/sh
connect -S 127.0.0.1:7086 "$@"  
# 我的ssh代理监听在本地的7086端口,可以实用ssh name@ssh.server.com -D port监听

修改~/.gitconfig,增加如下内容:

[http]
    proxy = socks5://127.0.0.1:7086
    # http协议直接走ssh代理
[core]
    gitproxy = /Users/xiafei/.socks5proxywrapper
    # git协议通过wrapper走代理

导出环境变量

# ssh协议代理wrapper
export GIT_SSH="/Users/xiafei/.socks5proxyssh"

这里获得connect的源文件,注释掉1765行,编译后放到$PATH上:

gcc connect.c -o connect
cp connect /usr/local/bin

现在你就应该可以实用git了,实测clone,push都没问题。

下面开始说正事儿:
gfw这帮傻逼,github这种站点都封,真不怕遭报应啊,真不怕生娃没菊花啊,真不怕被钉在耻辱柱上啊。好歹你们丫的还是技术人员,不能有diang性就没人性了啊!草泥马。

p.s. 原文地址:http://chunyemen.org/archives/813
p.s.s. 欢迎访问纯爷们的小生活

拜拜2012

转眼一年就过去了,工作之后的日子真是比在学校快多了,现在的杭州温度已经降到了零下,不过今天阳光明媚,吃过早饭,坐在窗户边的椅子上,泡上一杯龙井茶,开始回顾2012这一年。

去年写过6个愿望:

  1. 家人平安快乐,包括某猪
  2. 工作上能够承担更大的事情,把现在的事情做好,做大
  3. 努力学习电吉他,要脱离当前弱到爆的水平
  4. 回趟天津,而且得是上半年,不然真就见不到人了
  5. 最后的,也是最难的,希望年底地时候能够毫不犹豫地说出”what can make me CRAZY?”这个问题的答案

现在来一一回顾:

  1. 家人都身体健康,平平安安。谢谢老天的眷顾。
  2. 当时指的是实时流计算的项目,现在由于组织架构的原因,已经废弃。这里头有太多需要总结的了,后面会说。
  3. 年中的时候报了个吉他班,现在电吉他算是入门了吧,不过因为很少练习,水平一直很低。
  4. 没回去,失败了,没兑现。
  5. clojure,连我老婆都知道怎么念这单词了。

2012剩下不到12个小时了,不算这一篇,这一年我一共写了21篇博客,出去吐槽和絮叨的部分,剩下14篇都是技术相关,其中10篇是写clojure。算下来平均一个月一篇吧,有点懒了,明年应该是两周一篇才对得起自己。开始回顾!

1月

  1. 那个时候还有搜索平台部,年会很high,记得最后喝了好多酒,记得被提名了,可是没有得奖,记得搞了个吉他+口琴+太极版本的《大海》,很囧,是吧?
  2. 买了《三体》1和2,加上家里的3,已经凑齐了,很满足,可惜过年没有好好通读一遍。要不今年?
  3. 跟别人拼车回家,杭州到重庆一千多多公里,虽然一路上很顺利,但是最后弄得有点卓急,以后再也不拼车了。
  4. 带着活儿回家,放假还要抽时间写代码,每天汇报线上情况,怎么说呢…
  5. 过年回家最快乐的两件事情,一是见亲人朋友,二是各种好吃的。

2月

  1. 新一年的工作开始,来到DW的项目室,这段经历给了我很大的成长,当然,成长多半是痛苦的。不过,我很感谢。在不合适的时间点,每个人(包括我)又代表不同的部门,各自怀揣目的,不可能把一件事情做好。相互的不信任,没有一致的目标,是团队失败的根本原因。从自身来看,我们的系统概念复杂而且含糊,组件太多,可操作性太差,不必要的性能损耗过高,坑又太多,是我们自己失败的原因。或许,痛过才能证明爱过,这些痛也代表这我们对系统倾注的心血,也是我们成长路上必须要过去的坎儿。
  2. 开始学习clojure,全是中文资料几乎没有,真庆幸自己能够有耐心一点点看下去。

3月

  1. 合租的同事要离职,准备搬家,找房子真的是很麻烦的事情,很纠结,好在最后找到了现在的住处,价廉物美~
  2. 加班挺多,老婆经常在晚上等我一起回去,对你说声谢谢!
  3. 春天逐渐来临,跟悟时夫妇(人家现在是合法的了)以及侯一起到处逛逛,晒太阳。躺在草地上看风筝,看风筝背后湛蓝的天空,真是惬意。
  4. 黑皮的女儿出生了,拉开了今年周围的人生女不生男的序幕,预约了干爹的位置。

4月

  1. 开始做4clojure,有种当年坐acm题才学会cpp的感觉。其实学语言没有什么窍门,唯有coding!coding!coding!
  2. 团队的技术分享再度搞起来,当然零食少不了的,这种氛围很自在,很惬意,至今难忘。
  3. 老婆回学校做毕设了,留下我在杭州瞬间变成屌丝,各种煮面、泡面。偶尔做几个菜,却突然没有食欲。一个人去看电影,感觉跟周围格格不入。一个人,真是不舒服。
  4. 团队outing去泰国,5天6夜的旅行果然很赞,赞的不仅仅是资本主义特色的东西,跟多的是放松的心情和预约的旅程!

5月

  1. 开始捧着ipad读《Programming Clojure》和《Clojure in Action》,每天晚上基本上是2点睡,不是看书就是4clojure刷题,深深记得半夜写了个层层嵌套的代码被别人40个字符秒杀….以上这一切是因为…因为..没人管我…
  2. 跟悟时一起做系统的新版本,实践这自己幼稚的开发理念。
  3. 问老婆“今天想我几次?”,答:“一直在想啊,不是离散变量,是连续的。”
  4. 春天越发放荡起来,某易边上的草坪已经很茂盛了!年度的集体婚礼又来了,可惜那天晚上我在加班,不过最后跑过去看到了齐秦~
  5. 老婆顺利通过答辩。

6月

  1. 变化来临,团队被拆,吃完最后的午餐,拿着乔毅送的零食,照完最后的大合影,吃玩毛妹的宴请,送走长帅去北京,大家成为首批去一淘的人,在b2b最后的日子显得这么的不舍。
  2. 在一淘躁动了几个星期,最终还是暂时安定下来。回首这段时光,自己的想法和行动都是那么的幼稚,幼稚,幼稚,不过我庆幸最终我做出的选择是正确的。

7月

  1. 慢慢习惯每天俩小时在班车上的生活,偶尔还能在班车上睡上一觉,基友们都渐渐搬到城里去住了,最后剩下我一个人每天在滨江和城西之间来回。
  2. 在一淘的日子显得特别的清闲(也许就我是这样的吧),每天到公司要看一个小时微博,中午伙同以前的团队的基友们四出觅食,晚上打两个小时桌上足球,故障报警都跟我没关系,我只要每天写写文档,搞搞daemon。
  3. 继续学吉他,老师华了2/3的课程来教我古典吉他,但是我倒觉得挺好,至少哥会看五线谱了~
  4. 老婆入职~

8月

  1. 台风袭来,在家soho了一天~其实啥也没干。
  2. 每周五下班都觉得特别开心,因为回家刚好可以看《中国好声音》,至于奥运,就看了看林丹的决赛。
  3. 老妹来杭州实习,在这里呆了一个星期,很遗憾没怎么一起到处去耍,只是吃了顿火锅。
  4. 最终还是离开一淘,去了集团TimeTunnel团队,一去就开始学习维护线上系统,学习处理各种警告,比一淘的时候真是压力山大。
  5. 老婆上了茶艺课,俺送了一套茶具给她~这套茶具使用频率还是很高的。
  6. 今天给老婆讲了间谍偷lisp程序,偷到三页括号的笑话,她笑了,还说后三页肯定都是右括号。你太让我骄傲了!

9月

  1. 秋日将至,树叶渐黄,班车外是欣赏不完的风景,一年四季,每天都不重样。城里有个5A级景区,这一点是杭州最爽的地方,我就不说还一个很破很乱的火车站了。
  2. 在集团的工作逐步步上正轨,了解老系统,开发新版本,维护线上,这一切对我的成长来说是很宝贵的,我至少看见了一个线上实际运行的系统应该是什么样子的。
  3. 买了本《Go语言编程》两天看完了,之后我再也没碰过那本书,不过Go却是是个好东西,有机会我会考虑使用的,不过哥当然更喜欢Clojure,哈哈。
  4. 受不了各种断网了,果断买了个ssh账户,现在世界清净多了。可是前不久新闻说不能使用“非法代理”,我就查查技术资料,应该没事儿吧。。。。
  5. 好声音押宝梁博夺冠,InfoQ送了张QConf的门票,可惜那天哥有事情,没去成,浪费了。

10月

  1. 十一长假,跟老婆大吵一架,然后分别离家出走。好在后来和好了,才能够去参加毛妹的婚礼。
  2. 利用业余时间,开始做第一个自己的clojure项目——zk-web,完完整整做下来,感觉收获很大,在github上得了好些星星,很开心啊~

11月

  1. 继续给zk-web添加功能,clojure这东西玩起来真的很high,你会发现很多以前ugly and dirty的事情都会变得simple and elegant。
  2. 毛妹要离职了,哎,真心不舍啊,一起入职的几个同事,慢慢的,慢慢的,都在不同的地方,那种遗憾的感觉,是因为我们之间真不是同事那么浅薄的关系。祝福~
  3. 经历了淘宝的11.11,系统经受住了考验。凡是经历过11.11的码农,经验值肯定暴涨,自信心也会提高!
  4. 开始看《Joy of Clojure》,这本书果断买纸质版了。英文比前两本书要难一些,不过边查边看,会了解更多。clojure不仅仅是简单的jvm+lisp,背后关于状态、简单等问题的思考才是吸引我的根本原因。

12月

  1. 学会做鱼香肉丝了,应该可以拿到业余厨师的毕业证了吧~
  2. 在微博上弄了一个clojure的微刊,本来想把相关资料搜集起来跟大家分享,结果这个屎一样的产品搞的哥毫无兴趣。
  3. 我跟她求婚了,只是戒指买大了2号……法律流程什么的都是浮云了。

然后,是2013年的愿望,或者要达成的事情:

  1. 家人都身体健康,平安是福。
  2. 两周一篇博客。
  3. 工作上能够有更大的提升,在实战经验上能有质的飞跃。
  4. 苦心钻研clojure,向社区大牛们看齐。
  5. 学习中式面点~俗称包子馒头花卷儿什么的。

That’s all!

zk-web——简单的zookeeper web UI

先说废话

最近工作挺忙,上一篇博客俨然已经一个月以前了。不过忙起来,觉得充实了很多,人的精神状态也比之前一阵好了不少。总之,现在状态还挺好,空余时间还能看看书,写写东西。

再说起因

为啥要做一个zookeeper的web ui呢?命令行的东西一般来说还是很强大的,可是zk的client却有点弱。工作的时候,我一般都习惯在web上查看zk的数据,并做一些简单的操作——删除,新增,写数据之类的。之前我一直使用killme2008大牛的node-zk-browser。可是这玩意儿是node.js写的,而我对node.js一窍不通,也没想过要通,安装部署就废了老大的劲儿。再加上有时候想改点东西,就无从入手了。于是,程序员造轮子的贱毛病就犯了。想想,用clojure写吧,正好练练手。于是就开始了。

再说过程

简单思考一下,一个zookeeper的web ui无非包含以下三个部分:zk连接,web框架,css框架。下面说说三个部分的选型:

zk连接

我认为没有比curator更好用的zk客户端了,真庆幸悟时很早就把他介绍给我。curator虽然好用,api却略显啰嗦。不过用把curator wrap到clojure中,只留出最简洁的api,却是十分容易的,clojure很擅长这个。这部分代码在zk.clj中,目前只有区区57行。

web框架

clojure的web框架有很多,compojure, ring, noir等等,这些东西之间是什么关系我还有点傻傻分不清楚。noir定义了丰富的宏,把compojure和ring封装起来,api简洁优雅,深深吸引了我。无论是定义页面(defpage)、页面元素(defpartial)、生成html(使用hiccup)、post/get处理、session管理、cookie管理,都以简单得不能再简单的方式呈现出来,深得clojure的至简之道。感谢noir,感谢clojure,这部分代码只有100行多一点。

css框架

我是web白痴,我需要一个白痴级别的框架。经同事介绍,twitter的boostrap符合条件,再加上我们的TimeTunnel的管理系统也使用了boostrap,页面大方美观,于是css就使用boostrap了。使用boostrap后,基本不用写css了,只需要给你的页面元素写上各种class就好。

再说现状

zk-web现在read-only的部分基本完工了,如果你只是查看zk的数据的话,用zk-web基本能够满足你的需求了。简单贴点图:

首页上会记录最近访问的三个zeekeeper地址,直接点击即可访问,数据记录在cookie中。也可以输入新的地址。

节点的详情页面顶端是每一级祖先节点的链接,左边是孩子节点的链接,中间是节点的信息,右边是数据。

最后说将来

以后计划增加节点增删改的功能,以及针对xml,json的pretty-print。现在想到的就这么多,代码在https://github.com/qiuxiafei/zk-web,欢迎关注。

p.s. 欢迎访问纯爷们的小生活

p.s.s. 原文地址:http://chunyemen.org/archives/789

Mac Mountain Lion下配置Safari局部代理

首先问候一下方校长的祖上…让我没法欢度国庆!
于是昨天买了个ssh,倒腾了一下,现在上网畅快了。现在把配置方法纪录一下。

需要交代的:

  • 系统环境Mac OSX 10.8.2
  • 需要使Safari能够对部分网站使用代理(别跟我提chrome+swity!, 权限好烦)
  • 你需要有一个ssh帐户

第一步,启动本地代理转发

使用如下命令:

ssh -D 7086 ssh-user-name@ssh-host-name.domain

7086是ssh转发时监听的本地端口,我们只需要把请求转发到localhost:7086,就可以实现通过ssh代理访问目标网站。

第二步,配置.pac文件

.pac实际上就是js,需要定义一个FindProxyForURL(url, host)的函数。浏览器根据函数的返回值选择直连或者使用代理。我的.pac如下:

function FindProxyForURL(url, host)
{
    url = url.toLowerCase();
    host = host.toLowerCase();
    if(dnsDomainIs(host, "twitter.com")
        || dnsDomainIs(host, "blogspot.com")
        // and more
            )
        return "SOCKS localhost:7086";
}

第三步,制定.pac文件

将写好的.pac文件放到/Applications/Safari.app/Contents/Resources/下
你需要在系统偏好设置-网络-高级-代理中选择自动代理配置,单击选取文件,选择刚才的.pac文件。如果你找不到上述路径,command + shift + G,输入路径即可。

好了,打开Safari,浏览某些网站吧。
如果更改了.pac文件,需要重启Safari才能生效。

p.s. 欢迎访问纯爷们的小生活
p.s. 原文地址:http://chunyemen.org/archives/782

Challenge Python Challenge in Clojure

玩python的同学,肯定都知道PythonChallenge这个游戏,既学习了python,又锻炼了智商。今天,我试试用clojure来解决里面的题目。具体的解题思路就省略了,主要谈谈怎么使用clojure。

Level 0:   2的38次方

卧槽,clojure.core里头怎么找不到求乘方的函数!?一开始就这么不顺。那就取个巧吧,谁让这个底数是2呢…

user=> (bit-shift-left 2 38)
549755813888

于是得到下一题的url。

Level 1:  一个简单的解码。

图片已经提示了,是ansic码加2,具体实现需要处理一下非字母表字符和一点边界情况。用map来得最直观:

(def data ".........")
(defn decodec 1
    (cond
        (not (Character/isLowerCase c)) c
        (= c \y) \a
        (= c \z) \b
        :else (char (+ 2 (int c)))))
(apply str (map decodec data))

Level 2: 找字母

页面源代码中给出了一大坨乱码,并且提示说找出其中的字母,很简单,上filter:

(def data "………")
(apply str (filter #(Character/isLetter %) data))

得到”equality”,进入下一题

Level 3: 找特定字母组合

页面中同样给出一大坨字母,提示很明显了,找出左右被3个大写字母包围的小写字母。这种情况正则处理比较合适,比如re-seq

(def data "………")
    (->> (re-seq #"[a-z][A-Z][A-Z][A-Z][a-z][A-Z][A-Z][A-Z][a-z]" data)
         (map #(nth % 4))
         (apply str))

re-seq返回所有匹配的串,详见clojuredocs: re-seq。
我们得到下一题的链接:linkedlist

Level 4: Follow Web Link

很有意思,大意是要根据页面的提示,跳转到另外一个页面,一直follow到最终的页面。我们需要做两件事情

  • 通过url获取页面
  • 解析页面,获取新的url

获取页面推荐使用reader + line-seq的组合。reader函数返回一个java.io.BufferedReader,支持从文件名,String,URL,InputStream,Socket,byte array等方式构造,相当的帅啊!line-seq则是从java.io.BufferedReader返回一个按照换行符切分的字符串lazy sequence。这俩基本上算绝配。剩下的事情就是写个循环了….

(defn get-list [start]
    (loop [n start]
        (let [url (str "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=" n)
              data (first (line-seq (clojure.java.io/reader url)))
              target (re-seq #"\d+" data)]
            (println url data target)
            (recur (first target)))))

从”12345″开始,

(get-list "12345")

按照中间的提示:

; 16044 divided by 2
(get-list "8022")

最后进入下一关:peak

Level 5:python反序列化

这个有点坑爹了….完全python的语言特性,你让哥怎么搞!你让哥怎么搞!!
怎么搞?当然用python搞了!
这里使用一个小trick,通过shell调用python,然后….不说了,太猥琐了,上代码吧

(require '1)

(def stdin "import urllib,pickle
obj=pickle.load(urllib.urlopen(\"http://www.pythonchallenge.com/pc/def/banner.p\"))
for line in obj:
    print \"\".join(map(lambda pair: pair[0]*pair[1], line))")

(println (:out (sh "python" :in stdin)))

你能在终端上看到答案的….
啥时候试试clojure-py…应该能有不那么猥琐的解决方法..

p.s. 欢迎访问纯爷们的小生活
p.s.s. 原文地址:http://chunyemen.org/archives/769

三荣三耻以自省

以静心学习为荣,以无聊吐槽为耻。
以看书阅读为荣,以沉迷微博为耻。
以勤奋抠腚为荣,以胡言乱语为耻。

都是反射惹的祸

最近哥很郁闷…不过不想谈郁闷的事情…

哥一直觉得clojure的map肯定没有java原生的map快,为了验证想法,前一阵子哥做了一个实验:

user=> (import java.util.HashMap)
java.util.HashMap
user=> (def mp (HashMap.))
#'user/mp
user=>  (time (dotimes [n 10000000] (.put mp 1 1)))
"Elapsed time: 10024.797 msecs"
nil

可以看到,把HashMap的put操作做一千万次,耗时约10秒。再看看clojure的版本:

user=> (def mp {})     
#'user/mp
user=>  (time (dotimes [n 10000000] (assoc mp 1 1)))
"Elapsed time: 328.825 msecs"
nil

好吧,clojure原生的map竟然只要300多毫秒,话说那个时候哥还不知道clojure里头的反射原理,只觉得此事严重违背了知觉,相当坑爹。
反射的地方在这里:

(.put mp 1 1)

为什么呢?
clojure不知道mp的类型是啥,即使你之前把mp定义成了HashMap对象,可是在别的时候,你也可能改变绑定的关系,所以,clojure还是不知道mp的类型是啥。既然不知道类型是啥,那只有通过反射来找出mp对象名字叫”put”的方法。
clojure有一个打开反射警告的开关:

user=> (set! *warn-on-reflection* true)
true

打开之后,在发生反射的地方会打印警告:

user=>  (time (dotimes [n 10000000] (.put mp 1 1)))
Reflection warning, NO_SOURCE_PATH:5 - call to put can't be resolved.
"Elapsed time: 9853.678 msecs"
nil

通过type hint,我们可以告诉clojure某个对象是什么类型,就能避免反射:

user=>  (time (dotimes [n 10000000] (.put ^HashMap mp 1 1)))
"Elapsed time: 100.548 msecs"
nil

于是,没有警告了,速度也快了。

不过,个人觉得,太多的type hint让clojure原本融洽的代码变得有点恶心…唉…

p.s. 欢迎访问纯爷们的小生活

p.s.s 原文地址:http://chunyemen.org/archives/761

在clojure中调用shell命令

无论你信还是不信,程序中偶尔还是需要跑一个shell命令的。clojure.java.shell包就提供了这个功能,使用方法如下:

(require '[ clojure.java.shell :refer (sh)])
(sh "ls")
user=> {:exit 0, :out "lib\r\nproject.clj\r\nREADME\r\nsrc\r\ntest\r\ntest.iml\r\n", :err ""}

其中,:exit是命令的exit code,:out是标准输出,:err是标准错误。

如果需要传参数:

(sh "ls" "-lt")

还可以指定标准输入:

(sh "cat" :in "xxxx")

也可以改变工作路径:

(sh "pwd")
user=> {:exit 0, :out "/home/xiafei/package/clojure-1.3.0\n", :err ""}
(sh "pwd" :dir "/home/xiafei")
user=> {:exit 0, :out "/home/xiafei\n", :err ""}

p.s. 欢迎访问纯爷们的小生活

p.s.s 原文地址:http://chunyemen.org/archives/739

探究clojure的dorun与doall

上一篇文章提到让lazy sequence不再lazy,还有一种需要避免lazy的情况是需要立即调用带有副作用的函数,比如打条log。如果这个时候lazy了,结果也就不是我们所预期的了。

上次提到了doall,doall还有一个双生兄弟叫dorun,两者的定义很相似:

(doall coll)
(doall n coll)
(dorun coll)
(dorun n coll)

他们都是遍历参数sequence,调用do函数强制产生副作用,区别在于,doall返回的是传入的序列,dorun返回nil。doall的代码写得很明显:

(defn doall
  ([coll]
   (dorun coll)
   coll)
  ([n coll]
   (dorun n coll)
   coll))

它其实就是调用了一下dorun,然后把输入的coll返回了,不过这个时候的coll已经“实例化”(我也不知道该用什么词了)到内存中了。那么dorun是怎么做的呢?

(defn dorun
  ([coll]
   (when (seq coll)
     (recur (next coll))))
  ([n coll]
   (when (and (seq coll) (pos? n))
     (recur (dec n) (next coll)))))

貌似,它就遍历一下传给他的coll,啥也没干….其实做事情的是when,when会对循环的body部分显示地调用do,这样就强制产生了副作用,lazy的sequence不再lazy的原因就在于此。最后看看when的源代码,一切都很了然了:

(defmacro when
  [test & body]
  (list 'if test (cons 'do body)))

p.s. 欢迎访问纯爷们的小生活

p.s.s 原文地址:http://chunyemen.org/archives/735