Skip to content

Instantly share code, notes, and snippets.

@skandhas
Forked from hooopo/ns
Created May 8, 2012 12:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skandhas/2634504 to your computer and use it in GitHub Desktop.
Save skandhas/2634504 to your computer and use it in GitHub Desktop.
ns
有人用过cell么?
<span style="font-size: small;"><a target="_blank" href="http://rubyforge.org/frs/?group_id=2978&amp;release_id=9650">http://rubyforge.org/frs/?group_id=2978&amp;release_id=9650</a>
<br /></span>
<br /><span style="font-size: small;"> </span>
<br /><span style="font-size: small;">
<br />非常小,源代码一目了然。感觉虽然有点土,但比自己做个module的方式好一点点
<br />号称: 代替发挥component作用的controller, 这样就不需要邪恶的render_component了
<br /></span>
<br /><span style="font-size: small;">A cell acts as a lightweight controller in the sense that it will assign variables and render a view.</span>
<br /><span style="font-size: small;"> </span>
<br /><span style="font-size: small;">cell可以用session, params, request等方法访问 父控制器的对应参量
<br /></span>
<br /><span style="font-size: small;">但其它局部变量和实例变量 默认不会传到cell里面</span>
<br /><span style="font-size: small;">The controller's instance variables and params hash are not directly available from the cell or its views. </span>
<br /><span style="font-size: small;"> </span>
<br /><span style="font-size: small;">目录结构:</span>
<br /><pre name="code" class="java">app/
models/
views/
helpers/
controllers/
cells/
my_cell.rb #cell 'controller'
my_cell/ #cell 'views'
show.html.erb
...</pre><span style="font-size: small;">cell可以用application helper,若要使用其他helper需声明,如:</span><pre name="code" class="ruby">class ShoppingCartCell &lt; Cell::Base
helper :product
....
end</pre> <span style="font-size: small;">使用:</span><pre name="code" class="ruby">&lt;%= render_cell :my_cell, :show %&gt;</pre>
<br /><span style="font-size: small;">
<br /></span>
<br />
<br /><span style="font-size: small;">=======================邪恶的component回顾=========================</span>
<br /><span style="font-size: small;"> </span>
<br /><span style="font-size: small;">component 组装页面的使用方式大致如下:</span>
<br /><pre name="code" class="ruby">render_component :controller=&gt;..., :action=&gt;..., :params=&gt;{...}</pre>
<br /><span style="font-size: small;">rails似乎没有提供以path/url作component的方式,所以只好用</span>
<br /><pre name="code" class="ruby">render_component ( ActionController::Routing::Routes.recognize_path("/some/action") ) #how ugly!</pre>
<br /><span style="font-size: small;">想要包含外部页面为组件,其中一个办法是用esi:include标签。。。</span>
<br /><span style="font-size: small;">只有用render_component才能调用其他action及action之上的过滤器并包含其结果,而且很慢 ...</span>
<br />
为什么人们说Ruby是个异类
<p>rt.<br>
经过一番思考,我觉得有如下答案:</p>
<ol>
<li>
Ruby是面向人类的语言,其他语言却很少这样考虑。</li>
<li>
Ruby社区歧视Windows用户……</li>
</ol>
<p> </p>
<p><span style="color: #800080;">语法糖</span>
是经常被说奇怪的部分,也是新语言里经常带入的东西。</p>
<p>但是纵观很多语言,有的是完全反对,有的是简单代入——它们没有深刻思考这一问题。</p>
<p>Ruby语法糖不仅仅是写起来更短那么简单:</p>
<ul>
<li>它是在简单的通用规则上达成的:函数调用可以省略括号,函数名可以包含符号 —— 与其说它是规则,不如说它去掉了这两个规则:函数调用必须包含括号,函数名不能包含符号。</li>
<li>它是从自然语言出发的,更短但是读起来不明白的糖是不推荐的。</li>
</ul>
<p>所以Ruby的糖衣化的语句往往有两种读法:一是自然语言的抽象理解,一是具体的实现。</p>
<p>相比之下,Scala就显得是为糖而糖了,如下面的语句,仅仅为了省略一组{}就引入了一个局部语法规则:</p>
<pre name="code" class="Scala">args.foreach(arg =&gt; greeting += (arg + " "))</pre>
<p><span style="color: #3366ff;">2009年5月10日补充: </span>
</p>
<p><span style="color: #3366ff;">现在看来,scala 的这个语句不是糖: arg =&gt; ... 只是给了个匿名函数。</span>
</p>
<p><span style="color: #3366ff;">所以相比还是 ruby 更甜……(给匿名函数可以 {} 可以 do..end 可以 &amp;proc 可以 &amp;:symbol)</span></p>
<p> </p>
<p><span style="color: #800080;">end</span>
是经常被批评的部分,为什么Ruby用end结束块呢?</p>
<p>C和类C语言用'}' ,可是这个读起来拗口, 右大括号? close curlin ?</p>
<p>lisp和类lisp用')' ,读起来可以简单点,右括号或者ket,但是对于支持指令式编程的语言来说,用圆括号定义块容易混淆。</p>
<p>python省掉了这个,但代价就是空白字符——缩进也成为语法的一部分了。</p>
<p>哪一种更好,与用户习惯有关,如果你玩了几年C或者Java,自然就觉得end很奇异。</p>
<p>但是对于一个没有被先入为主影响的人,end是最浅显、最好读、最容易理解的……</p>
<p>譬如用end的BASIC就是简单易学到堪称脑残。</p>
<p> </p>
<p> </p>
<p>对没接触过程序设计的人,Ruby绝对比C,C++,Java,C#,Haskell容易学得多。但是,这类人经常是Windows用户,所以……很多人说Ruby学习难度高(不如说linux学习难度高吧……)</p>
给ruby实现汇编inline
<p>最近正在考虑给Ruby写个运行时<span style="color: #333399;">从汇编语言</span>
生成字节码的扩展。比<span style="color: #ff0000;">JIT</span>
更牛哦。</p>
<p> </p>
<p>原理:在C里面调用_asm call</p>
<pre name="code" class="c">BYTE buf[1024];
DWORD by = (DWORD)buf;
...
_asm call by
</pre>
<p> </p>
<p>思路:首先把字符串翻译成机器码,然后将机器码传给一个函数,这个函数大致上是这样</p>
<pre name="code" class="c">VALUE run_bytecode(VALUE self, VALUE code) {
DWORD buf = (DWORD)(StringValueCstr(code));
_asm call buf
return Qnil;
}</pre>
<p> </p>
<p>参考待读:</p>
<p>这里应该能找到不少有用的东西。</p>
<p>http://webster.cs.ucr.edu/AsmTools/RollYourOwn/index.html</p>
<p> </p>
<p>这个家伙写了20篇blog,完全用Ruby实现了汇编DSL,剩下的就是指令-&gt;字节码和算地址了。</p>
<p>http://www.hokstad.com/compiler</p>
Ruby 1.9: 中文编程
<p>突然想起,Ruby 1.9支持中文方法名和变量名!</p>
<p> </p>
<pre name="code" class="ruby">def 召唤 家丁
case 家丁
when '阿福', '旺财'
puts "……少爷,我系#{家丁}……"
else
puts '……(一段短短的沉默,然后一段长长的沉默)'
end
end
家丁甲, 家丁乙 = %w[阿福 旺财]
召唤 家丁甲</pre>
<p> </p>
<p>哼哼,我们可以改造黄瓜或者阿死别克了</p>
<p> </p>
<pre name="code" class="ruby">alias 龙门阵 Story
alias 角色扮演 Scenario
alias 假设 Given
alias 当 When
alias 而且 And
alias 于是 Then</pre>
<p> </p>
<p>不过还是有些限制,类和模块不能用中文名打头(开头加上大写字母还是可以的)</p>
<p> </p>
<pre name="code" class="ruby">class 家丁
end
#=&gt;error: class/module name must be CONSTANT
class Q宝宝
end
#ok</pre>
<p> </p>
<p>观众可能会问: if then 怎么整?先想想,大致用法应该是这样吧</p>
 
<pre name="code" class="ruby">如果 女的, 那么{问三围}, 否则{讲再见} </pre>
 
<p>实现起来,我们可以定义如果-那么-否则如下</p>
<p> </p>
<pre name="code" class="ruby">def 那么 &amp;块
end
def 否则 &amp;块
end
def 如果 条件, 真块, 假块=-&gt;(){nil}
条件 ? 真块.call : 假块.call
end</pre>
<p> 试一试</p>
<p> </p>
<pre name="code" class="ruby">def 问三围
puts '小……小柠檬?!小蜜瓜?!不想活了?!'
end
def 讲再见
puts '不是讲好一小时见血任做吗?你跑不掉的……hehehe……'
end
[true, false].each {|女的|
如果 女的, 那么{问三围}, 否则{讲再见}
}
</pre>
<p> </p>
<p> 继续汉化def——很简单</p>
<p> </p>
<pre name="code" class="ruby">alias 定义 define_method</pre>
<p> </p>
<p>文章太长不好,就此打住~</p>
<p> </p>
<p>参考文献:</p>
<p>《窈窕淑女》,《零之使魔》,《买凶拍人》</p>
warm和fuzzy \( ` △ ' )/
<p>受RednaxelaFX mm启发,修改产生了这么个东西: WFT(变种warm, fuzzy thing)</p>
<p> </p>
<p>warm也可看作return或者unit或者wrap,fuzzy也可看作bind或者pass。</p>
<p> </p>
<pre name="code" class="ruby">class WFT
  attr_reader :value
def initialize object
@value = (object.is_a? WFT) ? object.value : object
end
def fuzzy
WFT.new(yield @value)
end
def WFT.warm object
WFT.new object
end
end</pre>
<p> </p>
<p>它可以和object互相转换: </p>
<ul>
<li>WFT.warm object  #=&gt;  wft</li>
<li>wft.value  #=&gt;  object<br />
</li>
</ul>
<p>它满足四条定律(Monad?才……才不是Monad呢!毛毛律真微妙):</p>
<ul>
<li>暖暖律: WFT.warm(wft)  ==  wft</li>
<li>暖毛律: WFT.warm(object).fuzzy(f)  ==  WFT.warm(f(object))</li>
<li>毛暖律: wft.fuzzy(WFT.warm)  ==  wft</li>
<li>毛毛律: wft.fuzzy(f).fuzzy(g)  ==  wft.fuzzy(g(f))</li>
</ul>
<p> </p>
<p>它有什么用呢?嗯…… 可以改变运算顺序。</p>
<p>例如设 f(x) = x * x, g(x) = x + x, h(x) = x - 2,则h(g(f(12)))可以写成:</p>
<pre name="code" class="ruby">WFT.
warm(12).
fuzzy{|x|x * x}.
fuzzy{|x|x + x}.
fuzzy{|x|x - 2}
</pre>
<p> </p>
<p>或者:</p>
<pre name="code" class="ruby">[f,g,h].inject WFT.warm(12) do |acc, func|
acc.fuzzy{|x| func.(x)}
end.value</pre>
<p> </p>
<p>这好像有点自己找罪受诶,看看:</p>
<pre name="code" class="ruby">[f,g,h].inject 12 do |acc, func|
func.(acc)
end</pre>
<p> </p>
<p>不过好处还是有的……吧?</p>
<p>当你手写一个超级变态巨大复合运算的时候,机器不会嚷嚷"stack level too deep"(手写8000多层也太难了吧!)。</p>
<p>也可以消灭end和')'队形(这不就是传说中的恶趣味吗?)。</p>
<p>比较WFT和Monad,Monad通过手动包装,保持了上下文,WFT自动包装把上下文都毁了。</p>
<p>结果……只是栈变成了队列而已吗??</p>
<p> </p>
<p> </p>
<p>既然有WFT,那么也能造个FWT出来,唔……我认为它是个蒜子(Onion)。</p>
<p> </p>
<pre name="code" class="ruby">class FWT
def initialize arr=[]
raise 'wtf r u doing?' unless arr.respond_to? :each
@seq = [] #function sequence
arr.each do |f|
if f.respond_to? :call
@seq &lt;&lt; f
else
raise 'again, wtf r u doing?'
end
end
end
def fuzzy &amp;block
  @seq &lt;&lt; block
self
end
def warm object
@seq.inject object do |acc, f|
f.(acc)
end
end
end</pre>
<p> </p>
<p>用法:</p>
<pre name="code" class="ruby">FWT.new.
fuzzy{|x|x*x}.
fuzzy{|x|x+x}.
fuzzy{|x|x-2}.
warm 12</pre>
 
<p>对纯洁的函数(与IO没联系而且不会污染环境),似乎能搞一些定理、归约什么的,不过 that's another story...</p>
<p> </p>
<p>但我大约似乎肯定还是听到卤比sama在对摸哪的说:你不是我的那杯茶……</p>
sorakake 机体设定
【colony】
<br />
<br /><img src="http://www.sorakake.net/tech/img/colony_l.jpg" />
<br />
<br />
<br />colony 比例对照:
<br />
<br /><img src="http://www.sorakake.net/tech/img/colony2_s.jpg" />
<br />
<br />
<br />【QT-arms系列】
<br />
<br />秋叶:
<br /><img src="http://www.sorakake.net/tech/img/qt01_img.jpg" />
<br /><img src="http://www.sorakake.net/tech/img/qt01_b.jpg" />
<br />
<br />五月:
<br /><img src="http://www.sorakake.net/tech/img/qt02_img.jpg" />
<br />
<br />ほのか(各种翻译都拗口……):
<br /><img src="http://www.sorakake.net/tech/img/qt03_img.jpg" />
<br />
<br />二姐:
<br /><img src="http://www.sorakake.net/tech/img/qt04_img.jpg" />
<br />
<br />
<br />妹子的手办真想买一个……
<br /><img src="http://www.sorakake.net/goods/img/figure002.png" />
<br />
<br />官方HP站:
<br /><a target="_blank" href="http://www.sorakake.net/home.html">http://www.sorakake.net/home.html</a>
<br />
萌语言
系统需求:Ruby 1.9
<br />运行环境编码:GBK
<br />
<br />解释器moe.rb(请保存为utf-8格式)
<br /><pre name="code" class="ruby">
#encoding=utf-8
def 说 s
puts s.encode('gbk')
end
class String
def 萌
self.encode! 'utf-8'
[
'都', '.each ',
'做', ' do',
'酱', '","',
'~', "\r\n",
'喵', "\nend\n",
'哩', '"',
'咪', '{',
'叭', '}',
'咦', '(',
'哈', ')',
'咕', '[',
'咚', ']',
'呼', '|',
'说', '说 '
].each_slice 2 do |k,v|
self.gsub! k, v
end
eval self
end
end
while instr = (gets gets)
instr.sub! /\n.+?\n$/m, ''
instr.萌
end
</pre>
<br />
<br />运行(按Ctrl+Z可以退出)
<br /><pre name="code" class="命令行">
ruby moe.rb
</pre>
<br />
<br />萌语言的输入是here doc结构,第一行和最后一行必须相同。
<br />Hello World:
<br /><pre name="code" class="萌">
咕哩苹果酱香蕉哩咚都做呼果呼~说果喵
</pre>
<br />
<br />to do: 完善语法。
<br />
<br />未完待续……
萌语法(1)
写正规点的解释器之前,首先对萌语法进行一些设计
<br />
<br />下面一些东西是想到哪写到哪……
<br />
<br />1:<strong>是..就..不..喵</strong>
<br />控制结构,相当if then else end
<br />
<br />2:<strong>晕..呜..喵</strong>
<br />循环结构,无限循环,通过呜跳出。
<br />控制+循环示例:
<br /><pre name="code" class="萌">
晕是你就怪不呜喵喵
</pre>
<br />
<br />3:<strong>噗..噜..噜..噜..喵</strong>
<br />在数学上,我们常常从旧集合出发定义新集合,如{2x|x∈Z, x&gt;0},在萌语言中,我们可以这样写:
<br /><pre name="code" class="萌">
噗Z噜x噜x&gt;0噜2x喵
</pre>
<br />
<br />4:自由语境
<br />萌语言可以自由切换语境(Context,上下文),切换方法是行首/末空白
<br />纯空白行:建造新语境并代替该行对应的语境号
<br />[Tab]开头的行首空白表示往前n个语境
<br />[Tab]开头的行末空白表示往下n个语境
<br />[Space]开头的行首空白表示第n个语境
<br />[Space]开头的行末空白表示倒数第n个语境
<br />如果行首行末都有空白,则中间的内容被忽略(注释行)
<br />
<br />上面的n为二进制数,由[Tab]和[Space]表示
<br />[Tab]代表1,[Space]代表0
<br />
<br />表述有点不清晰……以后再改。
<br />
萌语言标准库:图形处理
使用<strong>嘣嘣</strong>语句可以定义一段动画,每两帧之间用一个空行隔开。
<br />
<br />举例如下:(摘自某人对破刃之剑某场景的解释)
<br /><pre name="code" class="萌">
嘣嘣
     ○ノ    \ ノ
      ノ\_・' ヽ /
       └    ○ヽ
                    ○ 、          ○
                三   /=>          ̄l □
                  /&gt; ´           く\
   .&lt; i~      ○ < キター
 丶 /   /\_ノ|丶
  ○ ̄      〈\
  ┃  | |
    ○  | |
\○, )\
 /   / ∠
〈〈
    _
   / /
 .'; /∨
○ノ  ◇ ○   ○
 \  へ~/  ̄ ヽノ|凹
  />   &gt;    Λ
            ○  /
       ○    く |L |
      /|V    /7/
    メ /&gt;~~`  √
     ○_
     &lt;\へ
         ̄ ミ    /
       ○   ミ   |
      /|V       /
    メ /&gt;~~`  √
     ○
    / \彡 -
   _|> x`         /
      ( ○       |
    /   |\      /
      /&gt;~~` √
嘣嘣
</pre>
未来IDE愿景
<p>自动完成和snippet:</p>
<ul>
<li>
IDE应该向中文输入法学习</li>
</ul>
<p>
<br />
自然语言理解:</p>
<ul>
<li>
从自然语言生成几乎可用的程序代码</li>
</ul>
<p> </p>
<p>容易学习及使用(对编程语言而言):</p>
<ul>
<li>不仅要容易看到示例代码,还要支持直接将示例代码转变成可用代码<br />
</li>
</ul>
<p> </p>
<p>0知识:</p>
<ul>
<li>不需要学习IDE相关的'jargon'或者快捷键</li>
</ul>
<p> </p>
<p>代码检查:</p>
<ul>
<li>其实也是我对编程语言的期待:分离代码检查语法并完善它(契约还是太弱太不灵活了),最终达到消灭test</li>
</ul>
<p> </p>
<p>消灭UML:</p>
<ul>
<li>灵活强大的程序设计语言应该可以做系统设计,IDE有责任帮助它达成这个目标,而不是引入一门别的语言</li>
</ul>
<p> </p>
<p>自学习:</p>
<ul>
<li>自学习不是观察用户习惯,顺从用户癖好——而是总结出用户可能看不到的新东西。一个有良心的IDE应该说:您的try...catch...是不是太多了?</li>
</ul>
<p> </p>
[元编程系列] nil? respond_to?
都是一些老东西.
<br />
<br /><strong>ICK</strong>是 invocation construction kit 的缩写,里面是什么呢,就是一些 <a target="_blank" href="http://en.wikipedia.org/wiki/Monad_(functional_programming)">Monad</a> 吧。
<br />
<br /><span style="color: cyan;"><a target="_blank" href="http://ick.rubyforge.org/">http://ick.rubyforge.org/</a></span>
<br />
<br />安装:
<br /><pre name="code" class="console">gem install ick</pre>
<br />
<br />一个经常出现的结构:
<br /><pre name="code" class="ruby">@person ? @person.name : nil</pre>
<br />
<br />我们觉得像下面这样写就够了:
<br /><pre name="code" class="ruby">@person.name</pre>
<br />
<br />为了达成上面的目标,我们可能会这样做:
<br /><pre name="code" class="ruby">class NilClass
def method_missing name, *args
nil
end
end</pre>
<br />
<br />不过有些代码倒是希望nil.name会弹出一个异常,所以也有另外一个方法:try()
<br /><pre name="code" class="ruby">class Object
def try method
__send__ method if respond_to? method
end
end</pre>
<br />
<br />结果就是写成
<br /><pre name="code" class="ruby">@person.try :name # 这样也显示了检查@person是否nil的意图
</pre>
<br />但是呢,使用ick,可以做到更强大的链式try哦~~
<br />
<br />先看一段比较烦的代码
<br /><pre name="code" class="ruby">a.b.c.d.e.f if a &amp;&amp; a.b &amp;&amp; a.b.c &amp;&amp; a.b.c.d &amp;&amp; a.b.c.d.e
</pre>
<br />再看一段更令人烦躁的代码
<br /><pre name="code" class="ruby">a.b.c.d.e.f if a.respond_to?(:b) &amp;&amp; a.b.respond_to?(:c) &amp;&amp; a.b.c.respond_to?(:d) &amp;&amp; a.b.c.d.respond_to?(:e) &amp;&amp; a.b.c.d.e.respond_to?(:f)
</pre>
<br />
<br /><strong>我们要把 if 后面那段去掉!</strong>
<br />
<br />为此先对Object添加特性: maybe和try
<br /><pre name="code" class="ruby">require 'ick'
class Maybe &lt; Ick::Guard
guard_with { |value, sym| value }
evaluates_in_calling_environment and returns_result
belongs_to Object
end
class Try &lt; Ick::Guard
guard_with { |value, sym| value.respond_to? sym }
evaluates_in_calling_environment and returns_result
belongs_to Object
end</pre>
<br />
<br />现在我们有舒服的写法了~~~
<br />
<br />下面方法链中,只要其中一个环节产生了nil,就返回nil
<br /><pre name="code" class="ruby">maybe(a){ |x| x.b.c.d.e.f }</pre>
<br />
<br />下面方法链中,只要其中一个环节方法没有定义,就返回nil
<br /><pre name="code" class="ruby">try(a){ |x| x.b.c.d.e.f }</pre>
<br />
<br /><strong>补充</strong>:
<br />
<br />为什么采用这样的结构?直接 try(a).b.c.d.e.f 不是更好么?
<br />
<br />因为:从try(a)起,b,c,d,e,f 都是原对应对象的代理,最后产生的东西和原本期待的 a.b.c.d.e.f 差别很大……
<br />
<br />所以,这段代码放在block里面,就可以把最后返回的值还原成干干净净的对象。
<br />
<br /><strong>2009.4.19 补充:</strong>
<br />
<br />ICK 让我们从另一个方式思考问题——Maybe Monad,但是,ruby 和 python 对象本身已经是 Maybe Monad 了…… 所以还是显得有点不必要。
<br />
<br /><pre name="code" class="ruby">a.b.c.d.e.f rescue nil</pre>
[元编程系列] 邪恶
<p>Ruby 很自由,我们爱死它的自由了,不过对某些邪恶的人来说,这点程度没法满足……<br>
下面的一些代码,出自一个古老的库:evil.rb,你可以 gem install evil-ruby 来获得它。<br><br>
不过原作者可能人间蒸发了,如果想要兼容1.9的 evil-ruby,请用 <a href="http://idm.s9.xrea.com/ratio/2008/07/11/000790.html">Yugui</a>
姐姐的修改版:<br><a target="_blank" href="http://github.com/yugui/evil-ruby/tree/master">http://github.com/yugui/evil-ruby/tree/master</a>
<br><br>
evil.rb 使用了核心库 Ruby/DL 来获得 C 层次的 Ruby 对象访问</p>
<p>不过……Yugui 姐姐你写的 DL,不仅大改 API,而且文档几乎为0…… <img src="/images/smiles/icon_cry.gif" alt=""><br><br>
作为实用主义者,先总结一下 API~ (源文件里注释写得很好,推荐直接看邪恶的源代码)<br><br>
你可以暂停GC的运行:</p>
<pre name="code" class="ruby">require 'evil'
RubyInternal.critical do
# GC暂停了!做坏事要打紧!
end
# critical 块和 RubyInternal 是下面很多方法实现的基础。</pre>
<p>
<br><br>
你还<span style="color: #800080;">可以看看一个对象在内存中是个什么东西</span>
:</p>
<pre name="code" class="ruby">require 'pp'
pp '12'.internal
</pre>
<p>
<br><br>
可以获得地址……:</p>
<pre name="code" class="ruby">'12'.internal_ptr.to_i # 47699480
'12'.internal_ptr.to_i # 47667740,显示这两个'12'是不同的对象
</pre>
<p>
<br><br>
可以获得内部类型在 C 中的定义数值:</p>
<pre name="code" class="ruby">nil.internal_type # 在1.8是32,在1.9是16
</pre>
<p>
<br><br>
还可以解冻对象~~(这个例子是作者举的):</p>
<pre name="code" class="ruby">obj = "Hello World".freeze
obj.frozen? # =&gt; true
obj.unfreeze
obj.frozen? # =&gt; false
</pre>
<p> </p>
<p><strong><span style="color: #0000ff;">解冻是超越安全等级的!</span>
</strong>
<br>
虽然在在高于 0 的安全等级用 unfreeze 时,会装模做样的弹出一个 Error,但是删掉 raise 那行就过去了……<br>
通过 RubyInternal 操作 flag 和 mask,还可以解除 tainted 等状态 -___- </p>
<p>怪不得 Programing Ruby 里面讲安全等级的那章说:</p>
<div class="quote_title">引用</div>
<div class="quote_div">You can also use some devious tricks to do this without using untaint. We’ll leave it up to your darker side to find them.<br>
</div>
<p>教训就是:<strong>千万不要 eval 信不过的代码!</strong></p>
<p><br><br>
检查对象是否直接量(关于直接量和非直接量,请参考 JE 知识库里的 Ruby Hacking Guide):</p>
<pre name="code" class="ruby">12.direct_value? # =&gt; true
</pre>
<p>
<br><br><span style="color: #800080;">可以改变对象所属的类!</span>
</p>
<pre name="code" class="ruby">class A;def 自白;puts '我是一个A';end;end
class B;def 自白;puts '我是一个B';end;end
a = A.new
a.自白 # =&gt; 我是一个A
a.class= B # 无敌的class=
a.自白 # =&gt; 我是一个B
</pre>
<p>
<br><br><span style="color: #800080;">也可以改变继承关系!</span>
</p>
<pre name="code" class="ruby">A.superclass = B
</pre>
<p>
<br><br>
类,就是蜡烛一般弱的东西……噗~~~就没了~~~</p>
<p><img src="/upload/attachment/87399/a44cc8da-329e-382f-883d-388aff79ddf1.jpg" alt=""><br><br>
一心同体……共享实例变量:</p>
<pre name="code" class="ruby">class Body
attr_accessor :heart
end
good_guy = Body.new
good_guy.heart = 'good'
bad_guy = Body.new
bad_guy.share_instance_variables good_guy # 建立连接,同步率直线上升!
puts bad_guy.heart # =&gt; kind
bad_guy.heart = 'bad' # 一个修改,全部改变
puts good_guy.heart # =&gt; bad
</pre>
<p>
<br><br>
复制单例方法:</p>
<pre name="code" class="ruby">a.grab_singleton_methods b
</pre>
<p>
<br><br><span style="color: #800080;">著名的 class to module:</span>
</p>
<pre name="code" class="ruby">class A
include Array.as_module # A可以当Array用了~
end
</pre>
<p>
<br><br>
Ruby不支持多重继承?当然支持了~</p>
<pre name="code" class="ruby">class C
inherit A, B # 后面的同名方法会覆盖前面的
end
</pre>
<p>
<br><br>
KernellessObject——更清洁干净的 BlankSlate 替代物,用来做基于 method_missing 的 DSL 载体非常合适。</p>
<p>在1.9里面返回一个 BasicObject。</p>
<pre name="code" class="ruby">clean_obj = KernellessObject.new # 在irb里会弹出一堆错,因为它连inspect方法都没有,irb想偷窥都不行
</pre>
<p>
<br><br>
UnboundedMethod#bind(obj) 原本要求 obj 属于该方法所在的类,不过 force_bind 超越了这个限制。<br>
下面是作者给的例子,我无耻的copy出来了:</p>
<pre name="code" class="ruby">foo_klass = Class.new do
def greet; "#{self.inspect} says 'Hi!'"; end
end
obj = []
greet = foo_klass.instance_method(:greet)
greet.bind(obj).call # raises TypeError
greet.force_bind(obj).call # =&gt; "[] says 'Hi!'"
</pre>
<p>
<br><br>
还有两个简单的self的运用,其实现和C无关:</p>
<pre name="code" class="ruby">class Object
def meta_class
class &lt;&lt; self
self
end
end
end
# meta_class(或者singleton_class,或者prototype)是一个class,
# 但是对它的修改只影响该对象,不会影响对象的类。
s = '不卫生'
s.meta_class.class_eval do
def xit
'xit'
end
end
s.xit # =&gt; 'xit'
'很卫生'.xit # =&gt; undefined method,String没有被改变
</pre>
<p> </p>
<p> </p>
<pre name="code" class="ruby">class Proc
def self
eval 'self', self
# eval的效果是——获得proc所在的对象,而直接用self只返回Proc本身
 end
end
</pre>
<p> </p>
[准翻译][精心提炼,绝对清纯] Haskell in 5 steps
<p><span style="color: #3366ff;"><strong>1. 准备工作</strong>
</span>
</p>
<p> </p>
<p>官网下载 <a href="http://haskell.org/ghc/download_ghc_6_10_1.html#binaries">GHC</a>
,大约有50M,然后阅读 <a href="http://www.haskell.org/haskellwiki/Haskell_in_5_steps">Haskell in 5 steps</a>
</p>
<p> </p>
<p> </p>
<p><strong><span style="color: #3366ff;">2. 准备之余</span>
</strong>
</p>
<p> </p>
<p>建一个目录,取名为“1”,进入这个目录</p>
<p> </p>
<p>写 hello.hs</p>
<pre name="code" class="haskell">main = print "hello"</pre>
<p> </p>
<p>写 fib.hs</p>
<pre name="code" class="haskell">fib 0 = 0
fib 1 = 1
fib n = fib(n-1) + fib(n-2)</pre>
 
<p> </p>
<p><strong><span style="color: #3366ff;">3. 跑程序</span>
</strong>
</p>
<p> </p>
<p>下载完毕,安装</p>
<p> </p>
<p>编译hello.hs (<span style="color: #ff00ff;">编译示例</span>)</p>
<pre name="code" class="console">&gt; ghc -o hello hello.hs
&gt; hello.exe</pre>
<p> </p>
<p>运行ghci (<span style="color: #ff00ff;">解释与交互示例</span>)</p>
<pre name="code" class="console">&gt; ghci
Prelude &gt; :load fib.hs
*Main &gt; fib 10
89
&gt; :q</pre>
<p> </p>
<p> </p>
<p><strong><span style="color: #3366ff;">4. 再跑一个高深点的程序</span>
</strong>
</p>
<p> </p>
<p>写parallel.hs(<span style="color: #ff00ff;">并行示例</span>,`par`连接——并行运行,`pseq`连接——顺序运行)</p>
<pre name="code" class="haskell">import Control.Parallel
main = a `par` b `pseq` print (a + b)
where
a = fac 10
b = fac 20
fac 0 = 1
fac n = n * fac (n - 1)</pre>
 
<p>编译并运行</p>
<p>    <span style="color: #003366;">-O2</span>:优化等级2,即最牛x的优化,</p>
<p>    <span style="color: #003366;">--make</span>:不用写 make file 的make,</p>
<p>    <span style="color: #003366;">-threaded</span>:多线程支持</p>
<pre name="code" class="console">&gt; ghc -O2 --make para.hs -threaded
&gt; para.exe</pre>
<p> </p>
<p> </p>
<p><strong><span style="color: #3366ff;">5. 总结</span>
</strong></p>
<p> </p>
<p>打开 ghc 的安装目录,转进 doc,打开 index.htm,ctrl+D 收藏(firefox),睡觉。</p>
懒惰的 Hash (以前怎么没发现这个好东西呢?)
<pre name="code" class="ruby">fac = Hash.new {|h, n| n == 0 ? h[n] = 1 : n * h[n - 1]}
p fac[4000]</pre>
<br />
<br />不错~
<br />
<br />不过fac[6000]就报错了……22619 levels
<br />
<br />相比之下,haskell算 fac 50000 都没问题,虽然会狂读硬盘大卡一阵……
<br />
<br />
<br />最后给个改进版:
<br />
<br /><pre name="code" class="ruby">
fac = Hash.new {|h, n|h[n] = n * h[n - 1]}
fac[0] = 1
100.times {|i|fac[i*500]} # 还是能算的嘛
fac[50000] # =&gt; failed to allocate memory -___-
</pre>
<br />
<br />另外给个题外的trick:
<br />
<br /><pre name="code" class="ruby">
a, *b = [1, 2, 3]
a # =&gt; 1
b # =&gt; [2, 3]
</pre>
tuple和list基础
读 <a target="_blank" href="http://darcs.haskell.org/yaht/yaht.pdf">yaht</a> 小记
<br />
<br />跳过洋洋洒洒的理念部分……直接进入控制台……
<br />
<br /><pre name="code" class="Prelude">
--comments
{-
block comments
-}
; --line breaks
--tuple, 固定长
(1, 2, 3, 4) --二人成 pair,三人成 triple,四人成 quadruple (谢谢R9X~)
fst(1, 2) --取出第一个元素
snd(3, 4) --取出第二个元素
--list,任意长,每个元素的类型必须相同
[1, 2, 3] --1 : 2 : 3 : []的语法糖
0 : [1, 2] --colon is a 'cons' operator
let l = [1, 2, 10] --ghci 用 let 定义,.hs文件里就不用
length l
head l
tail l
--string 就是内容为 char 的 list 的语法糖
"f" ++ "uck" --concat 操作,注:不会自动将 char 转为 string
show (5 ^ 5) --ruby.to_s,java.toString
read "66" --ruby.to_i
--常用 list 操作
map Char.toUpper "xiao case"
filter Char.isLower "UlPoPwEeRr"
--"lower"
let cubic_sum l = foldl (\y x -&gt; y + x ^ 3) 0 l
--立方和
--ruby.inject,google.reduce
let max l = foldl (\y x -&gt; if x &gt; y then x else y) 0 l
--最大值
foldr (-) 0 [1, 2, 3, 4]
--1-(2-(3-(4-0))),即 1-2+3-4
--foldr 可以对无限集操作,foldl不行
--foldl 速度快一点
let inf_l = 1 : inf_l --全为 1 的无限表
foldr (:) [] inf_l --等于inf_l
take 10 inf_l --取出前 10 个元素
inf_l !! 10 --取出"第" 10 个元素
</pre>
<br />
<br />补充:除了 yaht 和 wikibook,外,还可以看看 learn you a haskell for great good。
符号 in Haskell
<p>Haskell把很多数学符号化成了“颜文字”<br />
    (Emacs 好强,还能符号重现)<br />
<br />
\ 即是 λ<br />
<br />
-&gt; 即是 →<br />
<br />
函数名可以用' 结尾<br />
    (关于这个' 的读法,有人读“撇”,中学老师读“派”,长大了才知道读“prime”)<br />
<br />
| 即是大花括号</p>
<p> </p>
<p>&lt;- 就是 ∈</p>
<p> </p>
<p> </p>
<p>
btw,用了两天 ghci……牛头不搭马嘴的出错信息,诸多诡异的限制,连换行都不支持……我要大声疾呼:</p>
<p>你们这帮做 ghci 的,赶快把格拉斯高(Glasgow)升级成兰斯洛特(Lancelot)吧!</p>
<p> </p>
<p> </p>
<p><img height="244" src="/upload/attachment/88415/b1c5a89d-ad04-3ae7-9559-c3b16159ee02.jpg" width="189" alt="" />
  <span style="color: #ff0000;"><strong>---&gt;</strong>
</span>
<img height="243" src="/upload/attachment/88417/136919a1-04b3-3531-89d0-509686eee57d.jpg" width="195" alt="" />
  <span style="color: #ff0000;"><strong>please!</strong>
</span>
</p>
<p> </p>
<p> </p>
<p>-__-b 太虚了还用图片充内容……</p>
CPS
终于看完 yaht 第 4 章了,才知道原来类型系统都能玩这么多花样,而且末节 CPS 更体现了函数式编程的精髓。下面一些例子都是源于书中。
<br />
<br />CPS 是 Continuation Pass Style 的缩写。而 Continuation 和递归有点关系。
<br />
<br />函数的每次 call,都会把调用者的状态压一次栈。直到结尾处,才自己调用自己的函数是尾递归。
<br />
<br />一般递归可以 ... 变成只调用自己一次的递归,然后 ... 就变成了尾递归。
<br /><span style="color: grey;">——慢着,这马赛克是什么回事?</span>
<br />——因为地方太少写不下,只好用点点代替省掉的 n 行 ╮(╯﹏╰)╭ <span style="color: grey;">(群众纷纷扔番茄)</span>
<br />——OK、OK……其实我是这么想的:某天总会有人搜到这篇文章,看见 <span style="color: darkred;"><strong>3 个点</strong></span>,以为是搜索引擎省略的,打开来,才发现这确实是 <span style="color: darkred;"><strong>3 个点</strong></span>……<span style="color: grey;">(群众放下番茄,开始扔砖头)</span>
<br />
<br /><span style="color: grey;">尾递归有什么好处呢?</span>
<br />
<br />设 f1, f2 都是 f 的“实例”,f1 在尾部 调用 f2。这里用“shi力”指代它的局部变量和参数蹲在调用栈中、所占用的那个坑。
<br />f1 调 f2 的时候,f1 要拉的都拉完了,所以我们可以将占坑不拉的 f1 赶出来,让 f2 宽衣入坑——满塞!一个坑解决所有问题!此谓之“尾递归优化”。
<br />如此下去,就算递归个几亿重,也不会"坑溢出"(stack overflow)。
<br />
<br />可惜的是,由于 ... 的原因,大部分非函数式语言的编译器、运行时不支持尾递归优化,还有实现了又删掉的……<span style="color: grey;">(群众继续扔砖头)</span>
<br />
<br />幸好我们还有更彻底的方法:把 f1 改头换面,当成 f2 。这种代工拉 shi,把 f1 数据当成 f2 数据的方式 <a target="_blank" href="http://en.wikipedia.org/wiki/Continuation#History">启发了某些大牛</a> ,终于提出了 Continuation 这个东西。
<br />
<br />Continuation 看起来像个循环或者递归,但只有1个“shi力”,而且每次调用只走一步。
<br />中文名?个人以为借用柏格森的 <a target="_blank" href="http://en.wikipedia.org/wiki/Continental_philosophy">绵延</a> 就很好,也体现了单shi力“如滔滔江水,绵延不绝”的强大……
<br />
<br />于是大部分语言都有了自己的“绵延”,以显示生命和灵性所在(柏格森你是个大忽悠……)。这个貌似有状态的东西,数学上应该不能是个函数吧?
<br />嗯,有个温馨的东西(Monad)就是干这个的,但是还有 CPS 可以做这种工作 —— 它是 Style 而非 Type —— 的确是普普通通,来来去去的那道菜 —— 函数。
<br />
<br />看看书上的 CPS fold:
<br /><pre name="code" class="haskell">
cfold' f z [] = z
cfold' f z (x:xs) = f x z (\y -&gt; cfold' f y xs)
</pre>
<br />
<br />我觉得“尾行函数”更加符合道学(<a target="_blank" href="http://www.powerset.com/explore/semhtml/Taoist_sexual_practices">Taoist (x:xs) practices</a>),那一撇毛更是可有可无,就改成了:
<br /><pre name="code" class="haskell">
cfold ::
[x] -&gt;
acc -&gt; -- accumulator type
(x -&gt; acc -&gt; (acc -&gt; acc) -&gt; acc) -&gt; -- 尾行函数 type
acc
cfold [] acc f = acc
--最后两个 acc 可以用 t 或者什么符号代替,但是想个新名字又何苦呢
cfold (x:xs) acc f = f x acc (\acc -&gt; cfold xs acc f)
</pre>
<br />
<br />练习1. 请一口气读完:参数叁是参数叁是函数的叁参数函数的叁参数函数。
<br />
<br />这种“<a target="_blank" href="http://thottbot.com/?q=1382">超适应齿轮</a>”,可以让我们组装各种新函数,如foldl:
<br /><pre name="code" class="haskell">
nfoldl :: [x] -&gt; acc -&gt; (x -&gt; acc -&gt; acc) -&gt; acc
nfoldl list acc f = cfold list acc (\x acc g -&gt; f x (g acc))
</pre>
<br />
<br />试试nfoldl:
<br /><pre name="code" class="haskell">
nfoldl [1,2,3] [] (:)
-- -&gt; [1,2,3]
</pre>
<br />
<br />但直接用才体现了那个……给它指定如何去连接循环它才会循环……
<br /><pre name="code" class="haskell">
cfold [1..10] [] (\x acc cfold -&gt; cfold (x : acc))
-- -&gt; [10,9,8,7,6,5,4,3,2,1],先构造表再调用
cfold [1..10] [] (\x acc cfold -&gt; x : (cfold acc))
-- -&gt; [1,2,3,4,5,6,7,8,9,10],先调用再构造表
</pre>
<br />
<br />2009.4.6 补充:
<br />tangtong 问我为什么体现了“精髓”,我觉得是避免了Monad,使代码看起来更 FP 吧……
<br />
<br />最后找个台阶:初学Haskell,很多东西也是一知半解……这个东西还是蛮抽象的
<br />补充: 之所以把 f 放到最后,是因为ruby的习惯,于是这个东西也能很容易的写成ruby
11个8问题
昨天看到一个问题:
<br />用+-*/及括号组装11个8,最后等于2007
<br />
<br /><a target="_blank" href="http://libudi.iteye.com/blog/356905">传送门</a>
<br />
<br />试着用 Haskell 做一下:solve.hs(对不起这个只是检验有没有解而不是给出解……)
<br /><pre name="code" class="haskell">
module Main where
--先定义两个用的东西
contains [] elem = False
contains (x:xs) elem =
if x == elem
then True
else contains xs elem
merge l [] = l
merge l (x:xs) =
if contains l x
then merge l xs
else merge (x:l) xs
ops = [(+),(-),(*),(/)]
--用list comprehension是简单,但过程就米了……
mergeLists n = \m acc -&gt;
merge acc [ op x y | op &lt;- ops, x &lt;- res (n-m), y &lt;- res m ]
res 1 = [8]
res n = foldr (mergeLists n) [] [1..(n-1)]
--有解就True,没解就False
main = print (contains (res 11) 2007)
</pre>
<br />
<br />编译运行(很费cpu和时间,幸好是双核机器……)
<br /><pre name="code" class="console">
ghc -O2 --make solve.hs -o whether_there_is_a_solution
</pre>
<br />
<br />结果估计要算几年……
<br />
<br />补充:谢谢R9X mm。最后 mark 一下 Prelude 的 ref..
<br /><a target="_blank" href="http://www.haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html">http://www.haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html</a>
<br />
<br />题外,Haskell真的很舒畅,往往不用想什么内存吃多少的问题。
<br />得益于lazy和编译优化,定义了这么大一个List,运行时候才占4M内存,ruby如果用这种糟糕算法早就爆了/(ㄒoㄒ)/~~
<br />
<br /><span style="color: red;"><strong>4月12日修改:</strong></span>
<br />现在有过程了~ 能算 8 个 8(数秒), 但是更多的就会满内存了。
<br />
<br />输出是前缀表达式。
<br /><pre name="code" class="haskell">
module Main where
elem' y [] = False
elem' y@(n, _) (x:xs) =
if n == fst x
then True
else elem' y xs
merge l [] = l
merge l (x:xs) =
if elem' x l
then merge l xs
else merge (x:l) xs
merge' len l1 l2 =
if len &lt; 5
then merge l1 l2
else l1 ++ l2
ops = [
(\ (x, xx) (y, yy) -&gt; if y /= 0 then (x / y, ('/':xx) ++ yy) else (0, "div by 0")),
(\ (x, xx) (y, yy) -&gt; (x + y, ('+':xx) ++ yy)),
(\ (x, xx) (y, yy) -&gt; (x - y, ('-':xx) ++ yy)),
(\ (x, xx) (y, yy) -&gt; (x * y, ('*':xx) ++ yy))
]
mergeLists n = \m acc -&gt;
merge' n acc
[ op x y |
op &lt;- ops, i &lt;- [0..3], x &lt;- res (n - m), y &lt;- res m ]
res 1 = [(8, "8")]
res n = foldr (mergeLists n) [] [1..n-1]
findelem n [] = "No result"
findelem n (x:xs) =
if n == fst x
then snd x
else findelem n xs
main = do
putStrLn "How many 8:"
l1 &lt;- getLine
let n = read l1
putStrLn "The result you want:"
l2 &lt;- getLine
let m = read l2
putStrLn (findelem m (res n))
</pre>
<br />
<br /><img src="/upload/attachment/93566/8aa8e9f7-66d6-3b65-8307-12dd846d99e1.jpg" />
<br />
.NET 4.1 猫类 和 QCL 纠缠
<img src="/upload/attachment/90146/0fcfbe2f-2484-3cae-9b8f-f4c0c112af34.jpg" />
<br />
<br />今天看到的:
<br /><a target="_blank" href="http://www.cnblogs.com/blodfox777/archive/2009/04/02/1427972.html">http://www.cnblogs.com/blodfox777/archive/2009/04/02/1427972.html</a>
<br />
<br />愚人节“新”闻在这:
<br /><a target="_blank" href="http://www.hanselman.com/blog/NET41PreviewNewBaseClassLibraryBCLExtensionMethodsRFC.aspx">http://www.hanselman.com/blog/NET41PreviewNewBaseClassLibraryBCLExtensionMethodsRFC.aspx</a>
<br />
<br />不过,QCL 里面早就能模拟这个了~
<br />
<br /><strong>简单基础</strong>:
<br />
<br /><a target="_blank" href="http://www.ibm.com/developerworks/cn/linux/other/quant/index.html">http://www.ibm.com/developerworks/cn/linux/other/quant/index.html</a>
<br />
<br />量子计算机的寄存器(量子比特)基态可以为 |0&gt; 或 |1&gt;。
<br />
<br />每个寄存器状态是基态的叠加。如量子态:
<br />(1/sqrt(2)) |0&gt; + (1/sqrt(2)) |1&gt;
<br />
<br />这个量子态中,两个基态的系数都可以为复数,系数的模的平方的和等于 1。
<br />
<br />测量会使得量子态坍缩,变成基态之一。上面的量子态测量后,会变成 |0&gt; 或者 |1&gt;。
<br />再次测量 |0&gt;,仍然是 |0&gt;;再次测量 |1&gt;,仍然是 |1&gt;。
<br />
<br />可以认为:基态的系数的模的平方 == 测量得到该基态的几率。
<br />
<br />频谱:基态的几率叠加。如上面的量子态,频谱为:
<br />0.5 |0&gt; + 0.5 |1&gt;
<br />
<br /><strong>用带两个寄存器的量子计算机模拟</strong>:
<br />
<br />假设 a 为放射性原子,1小时内衰变的几率为 50%。用 |.0&gt; 代表衰变,|.1&gt; 代表没衰变。
<br />在 QCL 中,可以用 Hadamard 算符 (Mix) 制备这种状态:
<br /><pre name="code" class="qcl">qureg a[1];
Mix(a);</pre>
<br />
<br />现在 a = 0.707|.0&gt; + 0.707|.1&gt;
<br />
<br />假设 b 为猫,|0.&gt; 代表死,|1.&gt; 代表活。制备纠缠态
<br /><pre name="code" class="qcl">qureg b[1];
CNot(b,a);</pre>
<br />
<br />CNot(b,a)含义为“not b if a”。于是,如果 a 没衰变,即|.1&gt;,那么 b 为not |0.&gt;,即|1.&gt;,也就是活。
<br />
<br />现在猫的状态可以看做 b = 0.707|0.&gt; + 0.707|1.&gt;,半死不活。(确切的说不是这个,而是一个纠缠态)
<br />测量 a 将确定它的生死(&gt;^ω^&lt;)喵:
<br /><pre name="code" class="qcl">measure(a);</pre>
<br />
<br />
<br />村长对于此事故的评价:
<br /><span style="color: #666666;">这件事,我会派名侦探去调查了的喵
<br />这是下一封委托信喵
<br />当然做新武器也是必要的喵
<br />最近老觉得音响不够震撼喵
<br />要是有什么加强一下就好了喵
<br />真的很期待喵
<br /></span>
<br />
<br />ps:Ruby也可以玩随机类:
<br /><pre name="code" class="ruby">
a = [String, Array, Hash][rand(3)].new
</pre>
GC、shared_ptr 和 arena 是很强,不过……
大部分情况只是需要一个变长度的内存,用一下就丢了。
<br />所以用下面这个东西就可以了,而且速度更快…… 缺点是只增不减-__-。
<br />
<br />代码要重用,内存也要重用……
<br />
<br /><pre name="code" class="C++">
#pragma once
#include "utils.h"
#include &lt;cstdlib&gt;
//NOTE: this class should be used only on POD or C struct
//any class with a constructor / destroyer should NEVER invoke alloc
template&lt;class T&gt;
class temporal {
private:
void* buf;
unsigned long cap;
temporal(const temporal&amp;);
void operator=(const temporal&amp;);
public:
temporal() : buf(0), cap(0) {}
~temporal() { free(buf); }
inline T* alloc(unsigned long sz) {
if(sz &gt; cap) {
free(buf);
buf = malloc(sz*sizeof(T));
cap = sz;
}
return (T*)buf;
}
};
</pre>
IO 等两则
<strong><span style="color: blue;">1.IO</span></strong>
<br />
<br />Haskell 的函数,是数学定义的函数。
<br />
<br />IO 的行为有异于函数: f(a) != f(b)。
<br />
<br />main 必须返回 IO 类型。ghc 使用 IO 的常用形式:
<br /><pre name="code" class="haskell">module Main where
import IO
main = do
hSetBuffering stdin LineBuffering
--...</pre>
<br />
<br />和指令式语言不同,Haskell的 return x 将 x 变成 Monad(而 IO Monad class 将 return x 定义为了 IO x)
<br /><pre name="code" class="haskell">Prelude&gt; :t (return ())
(return ()) :: (Monad m) =&gt; m ()</pre>
<br />
<br />而 &lt;- 操作在 IO x 中提取出值 x 赋给左边
<br /><pre name="code" class="haskell">answer &lt;- getLine</pre>
<br />
<br />API小结:(Handle……果然是哪个词不直观就用哪个么……)
<br /><pre name="code" class="haskell">--File, FilePath 即是 String
data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
openFile :: FilePath -&gt; IOMode -&gt; IO Handle
readFile :: FilePath -&gt; IO String
writeFile :: FilePath -&gt; String -&gt; IO ()hClose :: Handle -&gt; IO ()
hClose :: Handle -&gt; IO ()
hIsEOF :: Handle -&gt; IO Bool
--Handle相关的函数
hGetChar :: Handle -&gt; IO Char
hGetLine :: Handle -&gt; IO String
hGetContents :: Handle -&gt; IO String
hPutChar :: Handle -&gt; Char -&gt; IO ()
hPutStr :: Handle -&gt; String -&gt; IO ()
hPutStrLn :: Handle -&gt; String -&gt; IO ()getChar :: IO Char
--无需Handle版本
getLine :: IO String
getContents :: IO String
putChar :: Char -&gt; IO ()
putStr :: String -&gt; IO ()
putStrLn :: String -&gt; IO ()
--用途见下面
bracket :: IO a -&gt; (a -&gt; IO b) -&gt; (a -&gt; IO c) -&gt; IO c</pre>
<br />
<br />bracket 是一个提升安全性的套套,它隐藏了一些异常处理的细节,并保证关闭文件等处理的执行。
<br /><pre name="code" class="haskell">getOneLineFrom1 = bracket
(openFile "1.txt" ReadMode)
hClose
hGetLine
</pre>
<br />
<br />上次看见有人说 Haskell 处理大量 IO 的东西会累死…… 乌索普! 看这 bracket 比 java 简单多少!
<br />
<br /><span style="color: blue;"><strong>2.一些细节备忘</strong></span>
<br />
<br />行首缩进用空格,不要用tab (得让编辑器把tab高亮出来才行),如果用tab,则要保证空格的缩进=8字符以和tab统一。
<br />
<br />main中,do 连起来的几行,如果不是 IO 语句结尾,就应该用 return () 产生一个空 IO ()
<br />
<br />case 的使用法
<br /><pre name="code" class="haskell">fun x = case x of 0 -&gt; "zero"; 1 -&gt; "one"; _ -&gt; "many"
--_ 也可以用 otherwise 代替</pre>
<br />
<br />Haskell 会把缩略的类型展开,譬如
<br />type InfType = Int -&gt; InfType
<br />就会导致 "infinite type"错误。
Win32 not paintful any more
Haskell有一个库 —— Win32
<br />
<br /><span style="color: gray;">更新:换用 ghc 6.10.2 后,Win32 的表现还不错~
<br />4 月 1 日的版本修正了大量的 bug </span>
<br />
<br /><pre name="code" class="haskell">module Main where
import System.Win32.DLL (getModuleHandle)
import Graphics.Win32
import Graphics.Win32.Message
import Graphics.Win32.Window
import Data.Int
import Data.Maybe
import Control.Monad
import Foreign.C.String
import Foreign.C.Types
foreign import stdcall "PostQuitMessage" postQuitMessage
:: Int32 -&gt; IO ()
main = do
let clsName = mkClassName "My Window Class"
hinst &lt;- getModuleHandle Nothing
whiteBrush &lt;- getStockBrush wHITE_BRUSH
curArrow &lt;- loadCursor Nothing iDC_ARROW
mAtom &lt;- registerClass (
cS_DBLCLKS,
hinst, -- HINSTANCE
Nothing, -- Maybe HICON
Just curArrow, -- Maybe HCURSOR
Just whiteBrush,-- Maybe HBRUSH
Nothing, -- Maybe LPCTSTR
clsName)
--faint so many Nothing...
when (isJust mAtom) $ do
hwnd &lt;- createWindow
clsName
"test window"
(wS_THICKFRAME + wS_CAPTION + wS_SYSMENU)
Nothing  
Nothing
Nothing
Nothing
Nothing
Nothing
hinst
wndProc
showWindow hwnd sW_SHOWNORMAL
updateWindow hwnd
allocaMessage pump
unregisterClass clsName hinst
pump lpmsg = do
fContinue &lt;- getMessage lpmsg Nothing
when fContinue $ do
translateMessage lpmsg
dispatchMessage lpmsg
pump lpmsg
render :: HWND -&gt; HDC -&gt; IO ()
render hwnd hdc = do
setBkMode hdc tRANSPARENT
setTextColor hdc $ rgb 0 0 0
textOut hdc 5 5 "hello world!"
wndProc :: HWND -&gt;
WindowMessage -&gt;
WPARAM -&gt;
LPARAM -&gt; IO LRESULT
wndProc hwnd wm wp lp
| wm == wM_DESTROY = do
postQuitMessage 0
return 0
| wm == wM_PAINT = onPaint
| otherwise = defWindowProc (Just hwnd) wm wp lp
where
onPaint = allocaPAINTSTRUCT $ \ lpps -&gt; do
hdc &lt;- beginPaint hwnd lpps
render hwnd hdc
endPaint hwnd lpps
return 0
</pre>
<br />
<br />解释:
<br />ghci -fglasgow-exts -luser32 gui_hello.hs
<br />
<br />编译:
<br />ghc -optl-mwindows -fglasgow-exts --make gui_hello.hs
<br />
<br />ps:
<br />e-text editor 的 haskell bundle 挺不错
<br />
<br />4 月 10 日补充:
<br />现在决定将我的编辑器迁移到 haskell 了~
CPS之2
<a target="_blank" href="http://night-stalker.iteye.com/blog/356708">上一篇</a>总结了一下CPS,感觉还是不够理解,于是继续看wiki book,加深一下体会。
<br />
<br />先定义一些缩略语:
<br />CPS: continuation passing style
<br />TR:&nbsp; tail recursion
<br />TC:&nbsp; tail call
<br />TCO: tail call optimization
<br />
<br />先看一个普通的递归是如何变成 TR 的。
<br />
<br />例如一个求和函数:
<br /><pre name="code" class="haskell">sum [] = 0
sum (x:xs) = x + (sum xs)</pre>
<br />
<br />它并不是 TR,因为它的最后一步是 + 。
<br />为了变成 TR,我们可以增加一个自变量来保存结果:
<br /><pre name="code" class="haskell">sum' res [] = res
sum' res (x:xs) = sum' (res+x) xs
sum list = sum' 0 list</pre>
<br />
<br />很不错吧?这样就能保证让编译器进行 TCO 了。
<br />这个trick 被滥用和推广以后——可以增加自变量,当然也能增加函数——就变成了 CPS。
<br />
<br />so we get:
<br /><div class="quote_title">Ricky Martin &lt;Livin' La Vida Loca&gt; 写道</div><div class="quote_div">Upside (down) Inside Out</div>
<br /><span style="color: gray;">这首歌真的很 hot,演唱会上面那个 mm 也很 hot……</span>
<br />
<br />练手:先将一个简简单单的函数改成 CPS。
<br /><pre name="code" class="haskell">-- origin
f x = ((x + 1) * 2 - 3) * 4
-- cps
f1 x cc = cc (x + 1)
f2 x cc = cc (x * 2)
f3 x cc = cc (x - 3)
f4 x cc = cc (x + 4)
f' x cc = f1 x (\x cc -&gt; f2 x (\x cc -&gt; f3 x (\x cc -&gt; f4 x cc)))
f'cps x = f' x (\x -&gt; x)</pre>
<br />
<br />感觉良好…… haskell 有专门语法杀括号:(其实还是很难看)
<br /><pre name="code" class="haskell">
f''cps x = f1 x $
\x -&gt; f2 x $
\x -&gt; f3 x $
\x -&gt; f4 x $
print</pre>
<br />
<br />最后一步传一个print,便是打印结果,so sweet~
<br />
<br />非常有趣:这个语法糖差不多就是后缀表达式了……
Lucas-Lehmer 法寻找 Messen 数
这个方法是最快的,比 Rabin-Miller test更快。
<br />原理参见 <a target="_blank" href="http://zh.wikipedia.org/w/index.php?title=%E5%8D%A2%E5%8D%A1%E6%96%AF-%E8%8E%B1%E9%BB%98%E6%A3%80%E9%AA%8C%E6%B3%95&amp;variant=zh-cn">wiki</a>
<br />
<br />数学公式几乎不用改,就能用 Haskell 写出来。(顺便晒一晒编辑器……)
<br />
<br /><img src="/upload/attachment/91960/2fbaa86a-2d32-33c6-a047-c4f987bb388b.png" />
<br />
<br />补充:写了个 2 线程的版本(如附件),编译时需要加 -O3 和 -threaded,运行时需要添加参数 +RTS -N2 (3个本地线程就是-N3)。
用 haskell 扩展 ruby
Haskell 是“纯洁的”函数式语言,可以解释运行,也可以编译成本地代码。
<br />其编译过程大致是先生成 C 代码(确切的说是 C--),然后使用自带的 gcc 编译。
<br />Haskell 编译后体积小,性能好,不需要笨重的运行时,在
<br /> <a target="_blank" href="http://shootout.alioth.debian.org/">http://shootout.alioth.debian.org/</a>
<br />的 benchmark 排名经常超过 java 和 D。
<br />
<br />与它相似的函数式语言的简单比较如下:
<br />
<br /><img src="/upload/attachment/92656/0031f875-faae-3884-927d-403cddbf3172.png" />
<br /><span style="color: gray;">注意:Erlang, Scheme 都是不纯洁的哦。
<br /></span>
<br />pure 和 lazy(惰性求值)密切相关。
<br />由于纯函数(就是我们中学课本上的函数,而不是一般编程语言说的“函数”)表现稳定,所以求值后可以存起结果。如果下次调用参数相同,可以直接返回这个结果(似乎编译器的优化要更复杂一些?)。于是纯函数式的语言用惰性求值不会产生任何性能问题。
<br />
<br />惰性求值有个好处是可以随心所欲定义无限长的列表,你用到其中某一项时它才会算出那项的值。
<br />eager 的就不行——定义时就会对这个 list 求值,无限循环了。
<br />
<br />---------------------------------------------------------------------------------
<br />
<br />Haskell vs Ruby:
<br />彻底的函数式 vs 彻底的面向对象,静态类型 vs 动态类型,极度纯洁 vs 极度不纯洁,面向数学公式 vs 面向自然语言,缩进 vs end ……
<br />
<br />一方的弱项正好是另一方的长处,所以用 Haskell 扩展 Ruby 还是很有意义的。
<br /><span style="color: gray;">当然两者也有一些共同的缺点,譬如:代码量都比较短。</span>
<br />
<br />首先请看一个简单的用 Haskell 产生dll的示例:
<br /><a target="_blank" href="http://www.haskell.org/ghc/docs/latest/html/users_guide/win32-dlls.html">http://www.haskell.org/ghc/docs/latest/html/users_guide/win32-dlls.html</a>
<br />
<br />不过 dllMain.c 和各种编译参数太古板了,可以写一个脚本省掉这个过程:
<br />
<br /><strong>makedll.rb</strong> 将当前目录下所有.hs文件都检查一遍,生成dllMain.c,然后自动编译链接产生dll。<strong><span style="color: indigo;">并且连带产生 interface.rb。</span></strong>
<br /><pre name="code" class="ruby">
### Begin Config
### Set output dll name
#outputdll = 'some name.dll'
### Set ghc options
#options = '-O2 -threaded'
### Set heap and stack options
#heap_and_stack = '-H128m -K1m'
### End Config
if ARGV[0] == 'clean'
system 'del *.o *.hi *.c *.a *.h'
exit
end
# tidy config options
outputdll ||= "#{File.basename File.expand_path('.')}.dll"
options ||= ''
heap_and_stack &amp;&amp;= "char *ghc_rts_opts = \"#{heap_and_stack}\";"
# hash for haskell-to-C type cast. needs to be complete in the future
TypeCast = { 'Int' =&gt; 'long',
'Char' =&gt; 'char',
'String' =&gt; 'char*'
}
# cast signature from haskell to ruby/dl style
def signature_cast line
if line =~ /^\s*foreign\s+export\s+stdcall\s+(\w+)\s+\:\:\s+(.+?)\s+\-\&gt;\s+IO\s+(\w+)\s*$/
func = $1.dup
ret_type = TypeCast[$3.strip]
plist = $2.split('-&gt;').map{|e| TypeCast[e.strip] }.join(',')
"extern \"#{ret_type} #{func}(#{plist})\", :stdcall"
end
end
# scan files
fnames = Dir.entries('.').select do |f|
!(File.directory? f) &amp;&amp; f =~ /\.hs$/
end
modules = []
fnames_with_ext = [] # files containing extern functions
rb_interface = [] # ruby interface modules
fnames.each do |fn|
File.open fn do |f|
# search for module xxx
module_name = nil
while line = f.gets
if line =~ /^\s*module (\w+)/
module_name = $1
break
end
end
# search for extern function signatures
externs = []
while line = f.gets
sig = signature_cast line
externs &lt;&lt; sig if sig
end
if externs != []
modules &lt;&lt; module_name
fnames_with_ext &lt;&lt; fn
rb_interface &lt;&lt; externs.join("\n")
end
end
end
# build ruby interface
File.open 'interface.rb', 'w' do |f|
ruby_template = &lt;&lt;-ES
require 'dl/import'
require 'dl/types'
module %s
extend DL::Importer
dlload '%s'
%s
end
ES
outputdll =~ /^([a-zA-Z]+)/
module_name = $1
module_name[0] = module_name[0].chr.upcase
f.puts ruby_template % [module_name, outputdll, rb_interface.join("\n ")]
end
# build dllMain.c
require 'erb'
File.open 'dllMain.c', 'w' do |f|
f.puts ERB.new(DATA.read).result(binding)
end
# compile
fnames_with_ext.each do |fn|
system "ghc -c \"#{fn}\" -fglasgow-exts"
end
system "ghc -c dllMain.c"
# link
objs = fnames_with_ext.map do |fn|
"\"#{fn.sub /hs$/,'o'}\" \"#{fn.sub /\.hs$/,'_stub.o'}\""
end.join ' '
system "ghc -shared dllMain.o #{objs} -o \"#{outputdll}\" #{options}"
__END__
#include &lt;windows.h&gt;
#include &lt;Rts.h&gt;
&lt;%= heap_and_stack %&gt;
&lt;% modules.each do |m| %&gt;
extern void __stginit_&lt;%= m %&gt;(void);
&lt;% end %&gt;
static char* args[] = { "ghcDll", NULL };
BOOL APIENTRY DllMain(HANDLE hModule, DWORD reason, void* reserved)
{
if (reason == DLL_PROCESS_ATTACH) {
&lt;% modules.each do |m| %&gt;
startupHaskell(1, args, __stginit_&lt;%= m %&gt;);
&lt;% end %&gt;
return TRUE;
}
return TRUE;
}
</pre>
<br />
<br />------------------------------------------------------------------------------
<br />
<br />简单过程示例:
<br />
<br />保证 ghc 6.10.2 (最流行的haskell编译器和解释器) 和 ruby 1.9 (内置DL库)。
<br />
<br />建一个文件夹adder,在里面新建 adder.hs,内容如下
<br /><pre name="code" class="haskell">
module Adder where
adder :: Int -&gt; Int -&gt; IO Int –– gratuitous use of IO
adder x y = return (x+y)
foreign export stdcall adder :: Int -&gt; Int -&gt; IO Int
</pre>
<br />
<br />把上面的 makedll.rb 扔进去,产生 adder.dll 和 interface.rb:
<br /><pre name="code" class="console">
ruby makedll.rb
ruby makedll.rb clean
</pre>
<br />
<br />用 ruby/DL 调用这个 dll 非常简单:
<br /><pre name="code" class="ruby">
require 'interface.rb'
puts "5+8=#{Adder.adder(5,8)}"
</pre>
<br />
<br />ruby/DL 调用扩展的好处是可以避免 "dll hell",我的ruby是VC2008编译的,调用gcc产生的动态链接库也不会出现 segfault。
<br />
<br />补充1:推荐使用 ghc 6.10.2 , ghc 6.10.1 可能有问题。
<br />补充2:在linux下编译 haskell 共享库要简单一些,不需要dllMain.c。具体可以参看
<br /><a target="_blank" href="http://blog.haskell.cz/pivnik/building-a-shared-library-in-haskell/">http://blog.haskell.cz/pivnik/building-a-shared-library-in-haskell/</a>
<br />补充3:修改 makedll.rb,可以产生 ruby interface 了。
<br />补充4:2009.9 看到 hubris: (只能在 linux 和 mac 下面用,类似于 rubyinline)
<br /><a target="_blank" href="http://www.infoq.com/news/2009/08/haskell-ruby-hubris">http://www.infoq.com/news/2009/08/haskell-ruby-hubris</a>
<br /><a target="_blank" href="http://github.com/mwotton/Hubris/tree/master">http://github.com/mwotton/Hubris/tree/master</a>
强大的 fold
fold 是重要的操作符(ruby 里面叫 inject)
<br />
<br />基本定义(Prelude.foldr):
<br /><pre name="code" class="haskell">
-- 从右往左卷
foldl f z [] = z
foldl f z (x : xs) = foldl f (f z x) xs
-- 从左往右卷
foldr f z [] = z
foldr f z (x : xs) = f x (foldr f z xs)</pre>
<br />
<br />另一种定义方式是通过 CPS 。
<br />
<br />令 z = 第一个元素,就得到变种 foldl1, foldr1。
<br />
<br />注意 foldl 虽然是尾递归,但是消耗内存和 foldr 一样:
<br />延迟求值使 f z x 不会马上被计算出来,依然会产生很大的调用栈。
<br />
<br />省内存(strict 版本, Data.List.foldl')的定义如下:
<br /><pre name="code" class="haskell">
foldl' f z [] = z
foldl' f z (x:xs) = let z' = f z x in z' `seq` foldl' f z' xs
</pre>
<br />
<br />--------------------------------------------------------------------------
<br /><strong><span style="color: #0033FF;">很多常用的函数都可以通过 fold 产生:</span></strong>
<br /><pre name="code" class="haskell">
sum = foldl (+) 0
product = foldl (*) 1
and = foldl1 (&amp;&amp;)
or = foldl1 (||)
(++) ys = foldr (:) ys
length = foldl (\n x -&gt; 1 + n) 0
reverse = foldl (\ys x -&gt; x : ys) []
map f = foldr (\x ys -&gt; f x : ys) []
filter p = foldr (\x ys -&gt; if p x then x : ys else ys) []
</pre>
<br />
<br />下面,let fold = foldr,看看还能玩什么花样。
<br />
<br />---------------------------------------------------------------------------
<br /><strong><span style="color: #0033FF;">“融”定理(the fusion property of fold)</span></strong>
<br />如果 <em>h</em> (<em>g</em> x z) = <em>f</em> x (<em>h</em> z)
<br />那么 <em>h</em> . <em>fold</em> <em>g</em> z = <em>fold</em> <em>f</em> (<em>h</em> z)
<br />
<br />应用举例:
<br />(* 3) . sum = (* 3) . fold (+) 0
<br />所以这里 h = (* 3), g = (+)
<br />
<br />令 f x y = x * 3 + y,则有
<br />h (g x z) = f x (g z)
<br />
<br />再根据上面的定理
<br />(* 3) . sum = fold f (h 0) = fold (\x y -&gt; x * 3 + y) 0
<br />也就是说,列表中所有元素的和乘以 3 ,等于每个元素乘 3 再求和。
<br />
<br />这个结论人脑想很浅显,但机器想就不是这回事了。
<br />fold 的这个性质给机器推理提供了新的方法。
<br />
<br />一些算法写出来简单易读,但是运行效率很低。利用这个性质进行变换,可以在不影响可读性的前提下优化算法。
<br />ghc 里面就有不少这样的优化。
<br />
<br />----------------------------------------------------------------------------
<br /><strong><span style="color: #0033FF;">“切香蕉”性质(banana split property of fold)</span></strong>
<br /><span style="color: gray;">梅姐 good job!</span>
<br />
<br />假设我们要计算和、平方和、立方和
<br /><pre name="code" class="haskell">sum'squareSum'cubicSum l = (sum l, squareSum l, cubicSum l)</pre>
<br />这样我们要遍历这个 list 3 遍。(这个问题在 C++ 模板编程中也经常遇到呢)
<br />
<br />但是用 fold 就不一样了:
<br /><pre name="code" class="haskell">sum'squareSum'cubicSum =
foldr (\x (s1, s2, s3) -&gt; (s1 + x, s2 + x ^ 2, s3 + x ^ 3)) (0, 0, 0)</pre>
<br />
<br />----------------------------------------------------------------------------
<br /><strong><span style="color: #0033FF;"> 源生递归 = fold </span></strong>
<br />在递归神教的教义中可以找到源生递归(primitve recursion)的通用模式:
<br /><pre name="code" class="haskell">
h y [ ] = f y
h y (x : xs) = g y x xs (h y xs)</pre>
<br />
<br />假定递归函数如上式定义,取出 f 和 g,然后用 fold 定义 k 如下:
<br /><pre name="code" class="haskell">
k y = fold i j
where
i x (z, xs) = (g y x xs z, x : xs)
j = (f y, [])
</pre>
<br />
<br />最后,我们得到的 k 是这么一个东西,它满足:
<br /><pre name="code" class="haskell">k y xs == (h y xs, xs)</pre>
<br />
<br />于是…… 万物至 fold …… (也不尽然,像 Ackman 函数那样的高阶递归就不能轻易的写成 fold)
<br />
<br />由于 fold 已经自带边界条件,所以用 fold 表达的函数不用写两行:
<br /><pre name="code" class="haskell">
--组合 list 内的所有函数
compose = foldr (.) id
</pre>
<br />
<br />foldl 也可以用 foldr 表示:
<br /><pre name="code" class="haskell">
foldl f v xs = fold (\x g -&gt; (\a -&gt; g (f a x))) id xs v
</pre>
<br />
<br />可惜如果不引入递归,foldr 没法用 foldl 表示——这也体现了命令式的循环的弱点。
<br />
<br />-----------------------------------------------------------------------------
<br /><strong><span style="color: #0033FF;">推荐 paper: </span></strong>
<br /><a target="_blank" href="http://www.cs.nott.ac.uk/~gmh/fold.pdf">http://www.cs.nott.ac.uk/~gmh/fold.pdf</a>
用 Ruby 踩踩四人帮
上上周在书店看到一本《Ruby设计模式》,捡起来 10 分钟看完,扔了(别问我为什么……)
<br />
<br />下面用 Ruby 写写设计模式,顺便批一批 Java 和 Gof。
<br />
<br />1.<a target="_blank" href="http://en.wikipedia.org/wiki/Factory_pattern">Factory</a> 和 Abstract Factory
<br /><pre name="code" class="Ruby">class Factory
attr_accessor :product
def produce
@product.new
end
end
class Product
#..
end
fac = Factory.new
fac.product = Product
fac.produce</pre>
<br />
<br />Java写的工厂有这么简单,这么容易重用么?
<br />
<br />2.<a target="_blank" href="http://en.wikipedia.org/wiki/Builder_pattern">Builder</a>
<br /><pre name="code" class="Ruby"># 工头
class Director
def build_with builder
acc = ''
[:header, :body, :footer].each do |m|
acc += builder.__send__ m if builder.respond_to? m
end
acc
end
end
# 工人
class HTMLBuilder
def header; '&lt;html&gt;&lt;title&gt;html builder&lt;/title&gt;';end
def body; '&lt;body&gt;html builder&lt;/body&gt;' ;end
def footer; '&lt;/html&gt;' ;end
end
class XMLBuilder
def header; '&lt;?xml version="1.0" charset="utf-8"&gt;';end
def body; '&lt;root&gt;xml builder&lt;/root&gt;' ;end
end
d = Director.new
puts(d.build_with HTMLBuilder.new)
puts(d.build_with XMLBuilder.new)
</pre>
<br />注意:Ruby的工序并不依赖于Builder的类,只要有方法签名就行了。
<br />interface 这种束手束脚,加强耦合的东西完全不需要~
<br />
<br />3.<a target="_blank" href="http://en.wikipedia.org/wiki/Prototype_pattern">Prototype</a>
<br />依样画葫芦。
<br />这里用一点 trick (evil ruby):
<br /><pre name="code" class="ruby">require 'evil'
class Prototype
# ...
end
class Concrete
include Prototype.as_module
end</pre>
<br />
<br />4.<a target="_blank" href="http://en.wikipedia.org/wiki/Adapter_pattern">Adapter</a>
<br />Ruby 包装方法易如反掌:
<br /><pre name="code" class="Ruby">class Adaptee
def talk; puts 'Adaptee';end
end
class Adapter &lt; Adaptee
alias talkee talk
def talk
puts 'before Adaptee'
talkee
puts 'after Adaptee'
end
end
Adapter.new.talk</pre>
<br />
<br />很多没学过设计模式的 Ruby 程序员天天用 adapter……
<br />
<br />5.<a target="_blank" href="http://en.wikipedia.org/wiki/Composite_pattern">Composite</a>
<br />这个 pattern 是给没有多重继承又没法 mixin 的语言用的。
<br />Ruby 只需要 include module。
<br />
<br />6.<a target="_blank" href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</a>
<br /><pre name="code" class="Ruby">module Colorful
attr_acessor :color
end
class Widget
end
w = Widget.new # w 作为 Widget 的实例,没有 color 方法
w.color = 'blue' rescue puts 'w has no color'
w.extend Colorful # 现在 w 有 color 方法了
w.color = 'blue'
puts w.color</pre>
<br />
<br />可怜的 Java 程序员需要学习设计模式才能写出 decorator。
<br />
<br />7.<a target="_blank" href="http://en.wikipedia.org/wiki/Flyweight_pattern">Flyweight</a>
<br /><pre name="code" class="Ruby"># 重量级对象的瘦身法:如果创建参数相同,则返回同一个对象
class FlyweightFactory
class Glyph
def initialize key
@key = key
sleep 1 # 睡一秒,以体现这个对象创建的“重量级”
@square = key ** 2
@cubic = key ** 3
end
attr_reader :key, :square, :cubic
end
def produce key
@glyphs ||= {}
@glyphs[key] || (@glyphs[key] = Glyph.new key)
end
end
ff = FlyweightFactory.new
g1 = ff.produce 2
g2 = ff.produce 2
puts (g1.object_id == g2.object_id)</pre>
<br />
<br />不得不说 || 是很爽的语法。
<br />另外 Ruby 的 Hash 可以用数组作 key,如果 Glyph 的构造函数需要更多参数,只需要把 produce 里的 key 改成 *key
<br />
<br />8.<a target="_blank" href="http://en.wikipedia.org/wiki/proxy_pattern">Proxy</a>
<br />Proxy 和 Adapter 的区别只在接口上,它们在 Ruby 中是一样的。
<br />这说明了:1.大道至简; 2.Gof 模式的语言局限性。
<br />
<br />9.<a target="_blank" href="http://en.wikipedia.org/wiki/Chain_of_responsibility_pattern">Chain of Responsibility</a>
<br />如果没有 proc,代码是做不到这么清晰简洁的:
<br /><pre name="code" class="Ruby">class Chain
def initialize
@chain = []
end
def add_handler &amp;block
@chain &lt;&lt; block
end
def handle req
@chain.each do |e|
# 如果handler返回 false(未处理),则让下一个处理
result = e[req]
return result if result
end
false
end
end
c = Chain.new
c.add_handler {|req| req == 1 ? "1:handled" : puts "1:not my responsibility" }
c.add_handler {|req| req == 2 ? "2:handled" : puts "2:not my responsibility" }
puts(c.handle 1)
puts(c.handle 2)</pre>
<br />
<br />10.<a target="_blank" href="http://en.wikipedia.org/wiki/Command_pattern">Command</a>
<br />本质:一次调用可以同时执行多个方法。GUI 编程中处理事件很常用。
<br />因为 Java 不能直接传递方法,所以把简单的问题复杂化了……
<br />btw:真不理解 swing 优美在哪里……
<br /><pre name="code" class="Ruby">class Command
def initialize
@executors = []
end
# 另一种方法是让 executors 保存一组对象,每个都带 execute 方法
# ——但是这么简单的事情就需要一组接口,一组实现?
def add_executor &amp;block
@executors &lt;&lt; block
end
def execute
@executors.each {|x| x.call }
end
end
c = Command.new
c.add_executor{ puts 'executor 1' }
c.add_executor{ puts 'executor 2' }
c.execute
</pre>
<br />Command 是和 Chain 很相似的东西,可能某天会有人写一本 "Pattern of Patterns" 吧。
<br />
<br />11.<a target="_blank" href="http://en.wikipedia.org/wiki/Template_method_pattern">Template Method</a>
<br />Java 一说模板,C++ 和 Ruby 就笑了。
<br />例(偷懒“重用”一下写过的代码):
<br /><pre name="code" class="Ruby"># 穷举法检验 de Morgan 定理
class Fixnum
%w[a1 a2 a3 b1 b2 b3].each_with_index do |name, idx|
define_method name, do
self &amp; (1&lt;&lt;idx) == 0 ? false : true
end
end
end
0b1000000.times do |n|
n.instance_eval %q[
if !((a1&amp;&amp;b1) || (a2&amp;&amp;b2) || (a3&amp;&amp;b3)) != !(a1&amp;&amp;b1) &amp;&amp; !(a2&amp;&amp;b2) &amp;&amp; !(a3&amp;&amp;b3)
puts 'blah'
end
]
end</pre>
<br />
<br />12.Iterator 和 Visitor
<br />这些模式还是作古吧。
<br />有太多简单的方式进行迭代(map, inject, each, each_with_index,sort ...)
<br />关键点还是语言对泛型和匿名函数的支持。
<br />
<br />13.<a target="_blank" href="http://en.wikipedia.org/wiki/Mediator_pattern">Mediator</a>
<br />将各个类的相互依赖性扔到一个中介类之中。
<br />老老实实的写个中介类?大概会像这样:
<br /><pre name="code" class="Ruby">class Mediator
def initialize seller, buyer
@seller = seller
@buyer = buyer
end
def sell
@seller.sell
end
def buy
@buyer.buy
end
end</pre>
<br />发现问题了吗? Mediator 出现的根源还是静态类型(是不会推断的那种)带来的耦合。
<br />Duck Typing (check respond_to? instead of class) 早已解耦,根本不需要中介。
<br />
<br />14.<a target="_blank" href="http://en.wikipedia.org/wiki/Strategy_pattern">Strategy</a>
<br />提供运行时选择策略(算法)的可能。
<br />假设 Array 有两种方法:bubble_sort 和 quick_sort
<br />按照 Gof 的教诲,我们可能会这样想:
<br /><pre name="code" class="Ruby">class Array
def sort options
if options[:strategy].to_sym == :bubble_sort
bubble_sort()
elsif options[:strategy].to_sym == :quick_sort
quick_sort()
end
end
end
arr.sort :strategy =&gt; strategy
</pre>
<br />根本就是没事找事……看看 Ruby 动态选择调用方法多简单:
<br /><pre name="code" class="Ruby">arr.__send__ strategy</pre>
<br />
<br />15.<a target="_blank" href="http://en.wikipedia.org/wiki/Singleton_pattern">Singleton</a>
<br /><pre name="code" class="Ruby">module SingletonClass
class &lt;&lt; self
# methods
end
end</pre>
<br />听说有人用两百行实现了严格的 singleton,膜拜中。
<br />
<br /><span style="color: white;">结论:
<br />如果没有类型声明和继承,很多依赖性都会消失无踪,接口也无用武之地了。
<br />设计模式大都是 Interface Hack 汇总,枯燥无味、思想僵化、限制创造力,早该下架了。
<br />学设计模式不如学 Ruby Python。
<br />Java 语言(注意不是平台)的弱点导致大量的冗余代码。
<br />所谓从善如流,有功夫写冗余代码不如多写几个测试。
<br />Java 语言(注意不是平台)的历史意义在于:将程序员从过早考虑效率的传统中解放了出来,现在也该功成身退了。</span>
<br />
<br />
SQL 与函数式编程
SQL 不愧是关系代数的产物,写出来就是赤果果的函数式编程。
<br />看这个语句:
<br /><pre name="code" class="sql">select * from topics where id &lt; 12</pre>
<br />
<br />把 topics 表看做一个 list,对应的命令式写法就像这样:
<br /><pre name="code" class="java">List&lt;Topic&gt; searchResult = new ArrayList&lt;Topic&gt;();
for(Topic topic : topics){
if(topic.id &lt; 12){
searchResult.add(topic);
}
}
return searchResult;</pre>
<br />
<br />但 select 本质上就是 filter 语句,在 Haskell 中可以写成:
<br /><pre name="code" class="haskell">filter (\x -&gt; (id x) &lt; 12) topics</pre>
<br />
<br />或者:
<br /><pre name="code" class="haskell">[x | x &lt;- topics, (id x) &lt; 12]</pre>
<br />
<br />而在 ActiveRecord 中对应查找方式是:
<br /><pre name="code" class="ruby">Topic.find :all, :conditions =&gt; ["id &lt; ?", 12]</pre>
<br />
<br />比较令人不爽,写成下面这样不好多了? 这才是 SQL DSL 嘛。
<br /><pre name="code" class="ruby">Topic.select(:all){ :id &lt; 12 }</pre>
<br />
<br />设想:将 block 解析成为 <a target="_blank" href="http://en.wikipedia.org/wiki/S-expression">s-exp</a>,然后翻译成 SQL 字符串。(s-exp 是一个数组套数组的结构,写出来就像 Lisp 程序一样,很适合用来做代码转换或者求值)
<br />
<br />这里提一提 ruby 中实现 s-exp 的简单原理:
<br />在一个新对象中对 block 求值,通过此对象的 method_missing 方法产生数组:
<br /><pre name="code" class="ruby">def method_missing meth, *args
[meth, args]
end</pre>
<br />实际上要复杂一些,还要反定义一些核心类的操作符等。(更全面、充分、快速的就要用到 parse tree 库了)
<br />
<br />基本可用的一个 sxp.rb 如<a target="_blank" href="http://www.iteye.com/topics/download/e410c38a-e302-38e5-8a4b-6cd3a1ab8e1c">附件(by Robin Stocker)</a>。尝试一下:
<br /><pre name="code" class="irb">irb(main):001:0&gt; require 'sxp.rb'
=&gt; true
irb(main):002:0&gt; id_max = 12
=&gt; 12
irb(main):003:0&gt; sxp{:id &lt; id_max}
=&gt; [:&lt;, :id, 12]</pre>
<br />
<br />工作良好,也能辨认闭包变量,再写一个 s-exp 到 sql string 的翻译器就行了。
<br />
<br /><pre name="code" class="ruby">class Symbol
def to_sql_method
{
:== =&gt; '=',
:_and =&gt; 'AND',
:_or =&gt; 'OR',
:_not =&gt; 'NOT'
}[self] || self
end
end
def build_sql arr
return arr.to_s unless arr.is_a? Array
meth = arr.shift
meth_s = meth.to_sql_method
params = arr.map {|e| build_sql e}
if params.empty?
"#{meth_s}"
elsif SxpGenerator::BINARY_METHODS.index meth
"(#{params[0]} #{meth_s} #{params[1]})"
else
"#{meth_s}(#{params.join ','})"
end
end
def Topic.select *options, &amp;block
Topic.find options, :conditions =&gt; build_sql(sxp &amp;block)
end
</pre>
<br />
<br />带 and 的使用方式:
<br /><pre name="code" class="ruby">Topic.select(:all){ _and(:id &lt; 12, :id &gt; 5) }</pre>
<br />
<br />这个实现比较 naive,不能辨认多行语句,and or 暂时只能前缀,也没有检查更多的 sql 函数……
<br />
<br />不过我的目的只是证明这种语法在 ruby 中是可以实现的。
<br />
<br />一般的数据库的使用方式是要通过 SQL 拼接的:
<br />逻辑 &lt;--&gt; ORM 框架、DAO 等 &lt;--&gt; SQL字符串 &lt;------&gt; 解析 SQL &lt;--&gt; 数据库 API 调用
<br />
<br />再进一步,对支持函数式编程的语言,为什么不直接一点,跳过生成 SQL 字符串这步呢?
<br />估计很多数据库操作的速度都会提升,也不会出现千奇百怪的 SQL 拼接法:
<br />逻辑 &lt;--&gt; 解析逻辑(语言编译器/解释器) &lt;------&gt;&nbsp; 数据库 API 调用
<br />
<br />补充: 看了 FX 的回帖,有点明白这个字符串形式调用的好处了。
<br />不过同一个进程/嵌入式的话,还是提供 select + 函数指针接口比较好呢。
不动点
简单的说,f 的不动点就是满足 f(x) = x 的值。(当然"值"也可以是函数)
<br />令 x = y f,那么在 Haskell 中可以写下 y 的定义:
<br /><pre name="code" class="haskell">y f = f (y f)</pre>
<br />
<br />Y-组合子最早是由 Haskell 发现的,除此之外还有其它不动点。
<br />
<br />不断展开可以看到 y f = f(f(f(f(...
<br />
<br /><img src="/upload/attachment/95461/2b761d11-4483-3006-8e22-cf91809c8883.png" />
<br />
<br />在 <a target="_blank" href="http://night-stalker.iteye.com/blog/366101">fold</a> 文中,一切递归形式都可以表达成 (<strong>foldr . 非递归函数</strong>)
<br />现在呢,一切递归也可以表达成 (<strong>不动点 . 非递归函数</strong>)。
<br />
<br />如阶乘函数
<br /><pre name="code" class="haskell">fac 1 = 1
fac n = n * (fac (n - 1))</pre>
<br />
<br />可以写成:
<br /><pre name="code" class="haskell">fac = y g where
g = (\f n -&gt; if n == 0 then 1 else n * f (n - 1))</pre>
<br />
<br />计算 fac 3 的过程如下:
<br /><pre name="code" class="haskell">fac 3
= y g 3 --fac
= g (y g) 3 --y
= 3 * (y g 2) --g
= 3 * (g (y g) 2) --y
= 3 * (2 * (y g 1)) --g
= 3 * (2 * 1) --g
= 6</pre>
<br />
<br />值得一提的是:这个实现是 haskell 算阶乘最快的…… 详见 <a target="_blank" href="http://www.willamette.edu/~fruehr/haskell/evolution.html">回字有几种写法</a>
<br />
<br />原因1:fix-point 是尾递,最终变成循环
<br />原因2:循环判定条件是 n /= 0,生成汇编会少一条指令
<br />
<br />继续了解不动点中…… 还得重温一点数学。
几点式? 无点式 (point-free style)
<p>跳译…… 原文见:</p>
<p><a target="_blank" href="http://www.haskell.org/haskellwiki/Pointfree" title="http://www.haskell.org/haskellwiki/Pointfree">http://www.haskell.org/haskellwiki/Pointfree</a></p>
<p> </p>
<p>函数式编程中,忽略接受的变量,将一个函数写成其它函数组合很常见。例如,这两个函数的功能是一样的:</p>
<pre name="code" class="haskell">sum = foldr (+) 0
sum' xs = foldr (+) 0 xs</pre>
<p> ...<br>
使用显式的点点往往使程序更清晰,如</p>
<pre name="code" class="haskell">let fn = f . g . h</pre>
<p> 就比</p>
<pre name="code" class="haskell">let fn x = f (g (h x))</pre>
<p> 好看。<br><br>
又一个例子:</p>
<pre name="code" class="haskell">--point-free map fusion
foldr f e . map g == foldr (f . g) e</pre>
<p> ...<br><br><span style="color: #3366ff;"><strong><span style="font-size: small;">1.但是呢,pointfree 用到的点更多……</span>
</strong>
</span>
</p>
<p><br>
一个常见的误会是:'pointfree' 风格的 'point' 指的是 (.) 算符。<br>
错! 这个词出自拓扑学。一个函数的 'pointfree' 定义,就是指:定义中不包含空间中的点(自变量,或者说,值)。<br>
Haskell 中,我们的 '空间' 是 type,'point' 是值。<br>
...<br>
额外两个例子:</p>
<pre name="code" class="haskell">f1 = (+ 1)
f2 = (1 +)</pre>
<p> </p>
<p><span style="color: #3366ff;"><strong><span style="font-size: small;">2.背景</span>
</strong>
</span>
</p>
<p> </p>
<p>...<br><br><span style="color: #3366ff;"><strong><span style="font-size: small;">3.支持工具</span>
</strong>
</span>
</p>
<p><br>
Thomas Yaeger: <strong>Lambdabot </strong>
(package pointfree)<br>
可以将函数声明写成 pointfree 形式(pl, point-less) 和 un-point-free 形式。<br>
示例<span style="color: #808080;">(漂亮和un漂亮)</span>
:</p>
<pre name="code" class="lambda bot">&gt; pl \x y -&gt; x y
id
&gt; unpl id
(\ a -&gt; a)
&gt; pl \x y -&gt; x + 1
const . (1 +)
&gt; unpl const . (1 +)
(\ e _ -&gt; 1 + e)
&gt; pl \v1 v2 -&gt; sum (zipWith (*) v1 v2)
(sum .) . zipWith (*)</pre>
<p> ...<br><br><span style="color: #3366ff;"><strong><span style="font-size: small;">4.探索组合子</span>
</strong>
</span>
</p>
<p><br><strong><span style="color: #3366ff;">4.1 猫头鹰</span>
</strong>
</p>
<p> </p>
<pre name="code" class="haskell">owl = ((.)$(.))</pre>
<p> 解释:</p>
<pre name="code" class="haskell">owl a b c d = a b (c d)</pre>
<p> 例:</p>
<pre name="code" class="haskell">&gt; ((.)$(.)) (==) 1 (1+) 0
True</pre>
<p><strong><span style="color: #3366ff;">4.2 点点</span>
</strong>
</p>
<p> </p>
<pre name="code" class="haskell">dot = ((.).(.))</pre>
<p>例:</p>
<pre name="code" class="haskell">sequence `dot` replicate ==
(sequence .) . replicate ==
replicateM
(=&lt;&lt;) == join `dot` fmap</pre>
<p><br><strong><span style="color: #3366ff;">4.3 晃</span>
</strong>
</p>
<p>...<br><span style="color: #808080;">没搞懂</span>
<br><br><strong><span style="color: #3366ff;">4.4 挤~果~酱~</span>
</strong>
</p>
<p> </p>
<pre name="code" class="haskell">f &gt;&gt;= a . b . c =&lt;&lt; g</pre>
<p>...<br><br><span style="color: #3366ff;"><strong><span style="font-size: small;">5.问题</span>
</strong>
</span>
</p>
<p>...<br><span style="color: #808080;">有时会导致搞不懂 /(ㄒoㄒ)/~~</span></p>
[Ruby 1.9] 鱼骨运算符
【根据 FX 的建议,正式命名为鱼骨运算符,&gt;-&gt; 长尾鱼骨,|-&gt; 为翻车鱼骨】
<br />
<br />Ruby 1.9 有一个改动:lambda 可以接受 proc。
<br />结合新的 lambda 语法: -&gt; x {...},我们可以做一些有趣的事情:譬如实现 Monad。
<br />
<br /><pre name="code" class="ruby">
# YOU ARE NOT SUPPOSED TO UNDERSTAND THIS
class Object
def return
-&gt; f {f[self]}
end
end
class Proc
# bind
def | f
call f
end
def un_return
call (-&gt; x {x})
end
def == p
(p.is_a? Proc) and (un_return == p.un_return)
end
# 方便的东西,可以把参数由[,,]变成[][][]
def -@
self.curry
end
end
</pre>
<br />
<br />这个“新”的运算符鱼骨(鱼头?) |---&gt; 其实是 | 和 - 和 -&gt; 的合体~~
<br />
<br />检验三律:
<br /><pre name="code" class="ruby">
m = 12.return
m |-&gt; x {x * 2} == 12 * 2
#=&gt; true
m |-&gt; x {x.return} == m
#=&gt; true
f = -&gt; x {(x * 2).return}
g = -&gt; x {x - 1}
(m | f) | g == m |-&gt; x {f.(x) | g}
#=&gt; true</pre>
<br />
<br />既然 Symbol#to_proc 作为标准写入了 ruby,看看这个:
<br /><pre name="code" class="ruby">m | :to_s.to_proc</pre>
<br />
<br />接着写一个 maybe Monad ? super easy !修改 return 的定义:
<br /><pre name="code" class="ruby">
class Object
def return mty = nil
if mty == :maybe
-&gt; f {nil? ? f[self] : nil}
else
-&gt; f {f[self]}
end
end
end
</pre>
<br />
<br />尝试一下:
<br /><pre name="code" class="ruby">
m = 12.return :maybe
m | :to_s.to_proc
#=&gt; "12"
m = nil.return :maybe
m | :to_s.to_proc
#=&gt; nil
</pre>
<br />
<br />但是 Maybe Monad 没什么作用,用 rescue nil 就可以达到同样的效果。
<br />而且由于返回了 nil,无法将状态传递下去,能传递下去的写法会非常非常复杂……
<br />
<br />一长串 Either Monad 适用的场景可以用简单的递归完成。
<br />
<br />注意定义里用了 [ ] 来 call 函数,我们可以传一个 array 或者 hash 、range 当做函数~~
<br /><pre name="code" class="ruby">
m = 3.return :maybe
arr = %w[a b c d]
m | arr
#=&gt; "d"
</pre>
<br />
<br />Array 形式的 pass on
<br /><pre name="code" class="ruby">m | arr.map(&amp;:return) |-&gt; x {print x}</pre>
<br />
<br />-&gt; x {print x} 很不爽吧,所以要改进:
<br />
<br /><pre name="code" class="ruby">class Proc
def | f
if f.is_a? Symbol
__send__ f, un_return
else
call f
end
end
def un_return
call (-&gt;x{x})
end
def == p
(p.is_a? Proc) and (un_return == p.un_return)
end
end
</pre>
<br />
<br />强大不?
<br /><pre name="code" class="ruby">
m | arr.map(&amp;:return) | :print
</pre>
<br />不强大。
<br />
<br />直接用 Proc 作 Monad 可能不太严谨,用个 Monad 包装可能这样要好点:(降低污染)
<br /><pre name="code" class="ruby">
class Monad &lt; Proc
...
</pre>
<br />
<br />==================================== 奇怪的分隔线
<br />
<br />List Monad 貌似更容易写
<br /><pre name="code" class="ruby">
class Array
def | f
if f.is_a? Symbol
(map {|e| __send__ f, e}).flatten 1
else
(map {|e| f[e]}).flatten 1
end
end
end
# return a : [a]
</pre>
<br />
<br />再次检验三律:
<br /><pre name="code" class="ruby">
m = [12]
m |-&gt; x {[x * 2]} == [12 * 2]
#=&gt; true
m |-&gt; x {[x]} == m
#=&gt; true
f = -&gt; x {[x * 2]}
g = -&gt; x {[x - 1]}
(m | f) | g == m |-&gt; x {f.(x) | g}
#=&gt; true</pre>
<br />
<br />这个貌似还有点用
<br /><pre name="code" class="irb">
&gt;&gt; def n x
&gt;&gt; [x+1,x+2]
&gt;&gt; end
&gt;&gt; [1] | :n
=&gt; [2, 3]
&gt;&gt; _ | :n
=&gt; [3, 4, 4, 5]
&gt;&gt; _ | :n
=&gt; [4, 5, 5, 6, 5, 6, 6, 7]
</pre>
<br />
<br />==================================== 奇怪的分隔线
<br />
<br />State Monad 是一个状态转移方程: -&gt; s { s, a }
<br />结果有点出乎意料…… 令人无比头痛的 State Monad 写起来真用不了几行:
<br />
<br /><pre name="code" class="ruby">class StateMonad &lt; Proc
def | f
StateMonad.new do |s0|
s1, a = self[s0]
f[a][s1]
end
end
end
class Object
def return_state
StateMonad.new {|s| [s, self]}
end
end
</pre>
<br />
<br />状态转移:
<br /><pre name="code" class="ruby">
# 注意这里用 - 将函数 curry 了
m = 3.return_state |--&gt; x, s {[s + 1, x * 2]}
state, val = m[0]
</pre>
<br />
<br />说到状态转移,可以用矩阵作转移函数! 设
<br /><pre name="code" class="ruby">t = Array.new(4) {|i|Array.new(4) {|j|[i, j]}}</pre>
<br />
<br />则:
<br /><pre name="code" class="ruby">
m = 3.return_state
(m | t | t | t)[0]
</pre>
<br />
<br />比下面的写法灵活呢:
<br /><pre name="code" class="ruby">
x, y = t[3][0]
x, y = t[x][y]
x, y = t[x][y]
</pre>
<br />
<br />==================================== 奇怪的分隔线
<br />
<br />总结: 各种 Monad 往往都有一个对应的 ruby 类:
<br />Unit&nbsp; - Proc
<br />List&nbsp; - Array
<br />Maybe - Object
<br />State - Proc
<br />
<br />历史:
<br />
<br /><a target="_blank" href="http://rednaxelafx.iteye.com/blog/347554">http://rednaxelafx.iteye.com/blog/347554</a>
<br />
<br /><a target="_blank" href="http://night-stalker.iteye.com/blog/349082">http://night-stalker.iteye.com/blog/349082</a>
<br />
GAE JRuby : links and notes
Google App Engine 支持 java 以后, 动作迅速的 Ola Bini 只花了一天,就把 JRuby 部署上去了。这是 Ola Bini 的原帖:
<br /><a target="_blank" href="http://olabini.com/blog/2009/04/jruby-on-rails-on-google-app-engine/">http://olabini.com/blog/2009/04/jruby-on-rails-on-google-app-engine/</a>
<br />
<br />jruby 最好用血淋淋的版本。它的版本库刚搬到 git 上面。可用下面两者之一:
<br />git://kenai.com/jruby~main
<br />git://github.com/jruby/jruby.git
<br />
<br />构建 jruby 要用 ant。 windows 下可以用比较傻的一个 ant:
<br /><a target="_blank" href="http://code.google.com/p/winant/">http://code.google.com/p/winant/</a>
<br />
<br />为了方便,可以弄个脚本设 jdk (jar) 和 ant 的路径,示例:
<br /><pre name="code" class="bat">
@set PATH=%PATH%;%JAVA_HOME%\bin;%ANT_HOME%\bin;D:\Develop\apache-maven-2.1.0\bin
@set CLASS_PATH=.;%JAVA_HOME%\lib\*.jar;
</pre>
<br />
<br />构建 jruby
<br /><pre name="code" class="console">
ant
ant jar-complete</pre>
<br />
<br />其它任务:
<br /><pre name="code" class="console">ant -p</pre>
<br />
<br />jruby-complete.jar 包含文件太多,需要拆。现成拆好的可以在 Ola 的 yarbl 里找:
<br /><a target="_blank" href="http://github.com/olabini/yarbl/tree/8681995b548860e2e13c90c4cf030fc682a32f34/lib">http://github.com/olabini/yarbl/tree/8681995b548860e2e13c90c4cf030fc682a32f34/lib</a>
<br />
<br />GAE 部署 sinatra 参考:
<br /><a target="_blank" href="http://blog.bigcurl.de/2009/04/running-sinatra-apps-on-google.html">http://blog.bigcurl.de/2009/04/running-sinatra-apps-on-google.html</a>
<br />
<br />sinatra 是什么? 你可以访问 JavaEye 的 ruby off rails 圈子看看,也可以花上 30 分钟看看这个视频:
<br /><a target="_blank" href="http://rubyconf2008.confreaks.com/lightweight-web-services.html">http://rubyconf2008.confreaks.com/lightweight-web-services.html</a>
<br />
<br />sinatra 非常简单,上手巨快,可以只用 200 多行写一个 wiki……
<br />核心思想: Exposed simplicity over hidden complexity
<br />
<br />关键的 rack for jruby 也不能少(其实用 Ola 给的 jruby-rack.jar 就可以了):
<br /><a target="_blank" href="http://github.com/nicksieger/jruby-rack/tree/master">http://github.com/nicksieger/jruby-rack/tree/master</a>
<br />
<br />Net::HTTP 的 monkey patch 及 User Service、Memcache support:
<br /><a target="_blank" href="http://github.com/lstoll/rb-gae-support/tree/master">http://github.com/lstoll/rb-gae-support/tree/master</a>
<br />
<br />DataMapper 的 Datastore Adapter:
<br /><a target="_blank" href="http://github.com/genki/dm-datastore-adapter/tree/master">http://github.com/genki/dm-datastore-adapter/tree/master</a>
<br />
<br />各种设置的样板文件:
<br /><a target="_blank" href="http://gist.github.com/91801">http://gist.github.com/91801</a>
<br />
<br />========================================= 下面是 Notes
<br />
<br />拆 jruby-complete.jar 的脚本—— windows 版
<br /><pre name="code" class="bat">
@del /F /Q jruby-core.jar
@del /F /Q ruby-stdlib.jar
@rd /S /Q tmp_unpack
@md tmp_unpack
@cd tmp_unpack
@jar xf ../jruby-complete.jar
@cd ..
@md jruby-core
@move tmp_unpack/org jruby-core/
@move tmp_unpack/com jruby-core/
@move tmp_unpack/jline jruby-core/
@move tmp_unpack/jay jruby-core/
@move tmp_unpack/jruby jruby-core/
@cd jruby-core
@jar cf ../jruby-core.jar .
@cd ../tmp_unpack
@jar cf ../ruby-stdlib.jar .
@cd ..
@rd /S /Q jruby-core
@rd /S /Q tmp_unpack
@del /F /Q jruby-complete.jar
</pre>
<br />
<br />warbler 0.9.12 有点问题,用简单粗暴的手段解决:
<br />打开 <pre name="code" class="java">%jruby_home%\lib\ruby\gems\1.8\gems\warbler-0.9.12\bin\warbler</pre>
<br />将 57 行改成
<br /><pre name="code" class="ruby">system "jruby -S gem unpack warbler"</pre>
<br />
<br />rake 也会卡你,打开 <pre name="code" class="java">%jruby_home%\lib\ruby\gems\1.8\gems\rake-0.8.4\lib\rake\repaired_system.rb</pre>
<br />将 109 行的 <pre name="code" class="java">*ENV["PATH"]</pre> 改成 <pre name="code" class="java">*(ENV["PATH"] || '')</pre>
<br />
<br /><strong>切记: jruby 和 netbeans 的摆放路径千万不能含空格和汉字。</strong>
<br />虽然 jruby 号称解决这个问题了,但还是小心的好……
<br />
<br />Active Record 用不了。
<br />
<br />打包 gem 成 jar 的示例(作用不大)
<br /><pre name="code" class="console">
jruby -S gem install -i ./rails rails --no-rdoc --no-ri
jar cf rails.jar -C rails .
</pre>
<br />
<br />==================================================== 结果
<br />
<br />我用 sinatra 做了一个简单的 hello 骨架:
<br /><a target="_blank" href="http://indekus.appspot.com">http://indekus.appspot.com</a>
<br />
<br />此骨架的源代码和各种 jar 已经传到 google code, 可以用 svn 拖下来:
<br /><pre name="code" class="console">
svn checkout http://gaesk.googlecode.com/svn/trunk/ gaesk-read-only
</pre>
<br />
<br />
[Ruby 1.9] 超箭头运算符
首先你需要 Ruby 1.9 ,然后
<br /><pre name="code" class="ruby">class Proc; def -@; self; end; end</pre>
<br />
<br />试一下:
<br /><pre name="code" class="ruby">p = ------------------&gt; x{x + 1}
p[1]</pre>
<br />
<br />have fun.
<br />
<br />
<br /><span style="color: red;">----------------------------&gt;</span>
<br /><span style="color: cyan;">--------------------------------------------------------------------------------------&gt; </span>
<br /><span style="color: violet;">-------------------------------------------------&gt;</span>
<br /><span style="color: orange;">&lt;------------------------------------------------------------------------------------------------------</span>
<br />
<br />
<br />
<br />有个更舒服的做法:
<br />gem install superators
<br />
<br /><div class="quote_title">Jay Phillips 写道</div><div class="quote_div">
<br /><pre name="code" class="ruby">require 'superators'
class Array
superator "&lt;-----------------" do |operand|
self &lt;&lt; operand.reverse
end
end
[1,2,3] &lt;----------------- [1,2,3]
#=&gt; [1,2,3,3,2,1]
</pre>
<br /></div>
<br />
<br />注意,和上面不同,多一个减号或者少一个都不成立哦。
<br />
<br />Jay Phillips 是 Rails 后另一个杀手级应用 Adhearson 的作者。
模板语言:蛤蟆 ( haml )
haml —— XHTML Abstraction Markup Language
<br />
<br />此文是熟悉 haml 时写的,一片凌乱。
<br />想看整洁漂亮、人性易懂、深入浅出的介绍请访问官网
<br /><a target="_blank" href="http://haml.hamptoncatlin.com/">http://haml.hamptoncatlin.com/</a>
<br />嗯,还有 DHH 当托 -____- |||
<br />
<br />开始:(安装时如果生成 rdoc 或者 ri 会出错)
<br /><pre name="code" class="java">gem install haml --no-rdoc --no-ri
haml --rails your_rails_app_home/app</pre>
<br />
<br />sinatra 天生支持:
<br />把 xxx.haml 扔到 views 目录里
<br /><pre name="code" class="java">haml :xxx</pre>
<br />就是这么简单!连config,require 都不用!
<br />
<br />除了和 web 框架集成,平时也可单独使用:(用法就和 ERB 差不多)
<br /><pre name="code" class="java">require 'rubygems'
require 'haml'
engine = Haml::Engine.new "%p Haml code!"
engine.render #=&gt; "&lt;p&gt;Haml code!&lt;/p&gt;\n"</pre>
<br />
<br />======================================== 语法
<br />
<br />基本语法:我认为下面两个例子是自明的……
<br /><pre name="code" class="java">%strong#message hello world
%strong.content.code= "hello world"</pre>
<br />
<br />%div 可以省略,大括号中的 hash 会转换成 xhtml 的属性
<br /><pre name="code" class="java">.item{:id =&gt; "item#{item.id}",
'style' =&gt; 'background-color:yellow'}= item.body</pre>
<br />
<br />缩进 == 嵌套
<br /><pre name="code" class="java">#content
.left.column
%h2= post.title
%p= post.content
.right.column= render :partial =&gt; "sidebar"</pre>
<br />
<br />如 erb 一样,可以访问 controller 的实例变量
<br />
<br />snippet 一个:
<br /><pre name="code" class="java">%html{:xmlns=&gt;"http://www.w3.org/1999/xhtml"}</pre>
<br />
<br />心理有障碍的…… 哦不,自闭的标签
<br /><pre name="code" class="java">%sandwich/</pre>
<br />
<br />聪明的省略:
<br /><pre name="code" class="java">%input{:selected=&gt;true}
#=&gt; &lt;input selected='selected'&gt;
%input{:selected=&gt;false}
#=&gt; &lt;input&gt;</pre>
<br />
<br />[a, b] 规则:
<br />class 为 cls = "#{b}#{'_' if b}#{a.class}".downcase
<br />id&nbsp;&nbsp;&nbsp; 为 "#{cls}_#{a.id}"
<br />设 @u = User.find 1,则
<br /><pre name="code" class="java">%div[@u, :name]</pre>
<br />产生
<br /><pre name="code" class="java">&lt;div class="name_user" id="name_user_1"&gt;</pre>
<br />而
<br /><pre name="code" class="java">%div[2]</pre>
<br />产生
<br /><pre name="code" class="java">&lt;div class="fixnum" id="fixnum_324"&gt;</pre>
<br />
<br />消灭标签外/内部前后的空格: &gt; &lt;
<br />如:
<br /><pre name="code" class="java">%p&lt;= "ha\nha"</pre>
<br />将会产生
<br /><pre name="code" class="java">&lt;p&gt;ha
ha&lt;/p&gt;</pre>
<br />&gt;&lt; 可以一起用
<br />
<br />~ "foo" 就是 =find_and_preserve "foo"
<br />
<br />======================================== helper
<br />生成 xml 指令和 doctype 指令
<br /><pre name="code" class="java">!!! XML
!!! 1.1</pre>
<br />
<br />指令还有
<br /><pre name="code" class="java">!!! Strict
!!! Basic
!!! Mobile</pre>
<br />
<br /><pre name="code" class="java">/ html comment</pre>
<br />产生(单行)comment
<br /><pre name="code" class="java">&lt;!-- html comment --&gt;</pre>
<br />
<br /><pre name="code" class="java">/
%p doesn't render</pre>
<br />产生多行comment
<br /><pre name="code" class="java">&lt;!--
&lt;p&gt;doesn't render&lt;/p&gt;
--&gt;</pre>
<br />
<br /><pre name="code" class="java">\</pre>
<br />转义一行的首字符
<br />
<br /><pre name="code" class="java">|</pre>
<br />置于行尾,连接多行字符
<br />
<br /><pre name="code" class="java">:some_filter</pre>
<br />(注意后面的行要缩进) 已定义的 filter 包括
<br />plain, javascript, escaped, ruby, preserve, erb,
<br />sass, textile, markdown, maruku
<br />
<br />多行 =
<br /><pre name="code" class="java">%p
= ['ho','hi'].join ' '
= 123</pre>
<br />设置 :escape_html 选项后,所有 = 语句都会转义
<br />
<br /><pre name="code" class="java">-</pre>
<br />对 ruby 代码求值但不输出
<br />
<br />== interpolates rb code into plain txt,如
<br /><pre name="code" class="java">%p== This is #{h quality} cake!</pre>
<br />这时 \ 可以转义 #{}, 但是 \ 本身不转义,如
<br /><pre name="code" class="java">%p== \\ no \#{yes}</pre>
<br />产生
<br /><pre name="code" class="java">&lt;p&gt;\\ no #{yes}&lt;/p&gt;</pre>
<br />
<br /><pre name="code" class="java">&amp;=</pre>
<br />输出前将右边内容转义,如果设了 :escape_html,则同 =
<br />
<br /><pre name="code" class="java">!=</pre>
<br />不将右边内容转义,如果不设 :escape_html, 则同 =
<br />
<br />block~~~~ 风格很 python ...
<br /><pre name="code" class="java">- (1..100).each do |i|
%p= i</pre>
<br />
<br /><pre name="code" class="java">-# single line comment
-#
multi line comment
nor will this line display</pre>
<br />
<br />======================================== 选项设置
<br /><pre name="code" class="java">Haml::Template.options[:format] = :html5
</pre>
<br />这条有点帅,但是 w3c.org 自己都通不过 html5 检验呢(format 默认 xhtml)
<br />
<br />在 sinatra 设置要简单一些
<br /><pre name="code" class="java">set :format, :html5</pre>
<br />
<br />其它选项
<br />:escape_html, :suppress_eval ……
<br />
<br />======================================== haml2other
<br />
<br />以前看到一个帖子说 haml 是美工的噩梦?你可以在命令行运行 haml 输出 html:
<br /><pre name="code" class="java">haml in.haml out.html</pre>
<br />
<br />记住这些东西: css2sass, html2haml, sass2css, haml2html 对团队协作非常有意义。
<br />
<br />用蛤蟆产生 erb -- 只需 9 行代码,不难吧?
<br /><a target="_blank" href="http://gist.github.com/17371">http://gist.github.com/17371</a>
<br />
<br />蛤蟆标签的 id 和 class 完全是 css 选择器形式,看起来很直观,
<br />如果你们的美工肯花 20 分钟看 tutor,那就没障碍了,erb 可以让他们更抓狂吧……
<br />当然有人不同意以上看法(他认为是 2 分钟)。
<br />
<br />似乎还没 IDE 支持。但 haml 趋近于精简的极点,能让 IDE 自动完成的东西都让 haml 咔嚓掉了……
<br />或许不久后有爱人士会弄一个可以画的…… 不过画的速度肯定没打字的快。
<br />不过我想 haml 的 IDE 很容易做。
<br />
<br />查看命令行 haml 的更多选项:
<br /><pre name="code" class="java">haml -help</pre>
<br />
<br />======================================== 一些链接
<br />
<br />不相信可读性提高的,可以看这个例子:
<br /><a target="_blank" href="http://vivimusing.iteye.com/blog/324845">http://vivimusing.iteye.com/blog/324845</a>
<br />
<br />reddit 上关于 haml 和 sass 的口水仗(老外还有整个团队使用 haml + sass + compass 的,这对从众不从良的中国人太困难了……)
<br /><a target="_blank" href="http://www.reddit.com/r/web_design/comments/88dy7/what_is_your_opinion_of_writing_hamlsass_as/">http://www.reddit.com/r/web_design/comments/88dy7/what_is_your_opinion_of_writing_hamlsass_as/</a>
<br />
<br />======================================== 某些感想
<br />
<br />要达到更高的思考/工作效率,速记法和黑话是必不可少的,不过满屏黑话也会让人望而生畏……
<br />虽然这些定义很简单,学起来很快,结果也很清晰,但是让人产生恐惧心理就影响推广了。
<br />
<br />注意等号前面不能有空格,否则就当普通行处理。
<br />
忽悠名词小汇
忽悠名词是这么一些东西:它们都不是一句话,或者一小段话能说明白的概念(事实上没人能说明白……)。
<br />维特根斯坦老爹根本不鸟这类模糊的概念("我是谁"、"生命的意义"等等……)。
<br />萧老师说我们生产新东西,一定要 "well-defined"(可是商业社会想赚钱还得靠忽悠)。
<br />
<br />小修小改,云山雾罩。列举一些 buz words 和其近义词如下 ↓
<br />
<br />中间件(middleware):外挂的拦截器
<br />
<br />网格(grid)计算:封装的分布式计算
<br />
<br />云计算:大规模的网格计算
<br />
<br />架构:没代码的程序
<br />
<br />架构师:UML 程序员
<br />
<br />面向服务(SOA):非面向对象
<br />
<br />领域(domain):要解决的问题
<br />
<br />商业智能(BI):抓主意小工具
<br />
<br />
<br />胡乱组合也是忽悠技巧之一。
<br />譬如有人说“云计算不是简简单单的网格!”,问他到底是什么他又答不出来,只说概念很广泛,还“结合了 SaaS,PaaS,……”,而且不是简单组合,而是“有机整合”……
<br />
<br />数学中有一个思想是求同:把问题同构映射到我们熟悉的数学结构上,往往就能充分利用前人的智慧,解决这个问题。
<br />但是忽悠有个思想是求异:一定要忽略本质上相同的东西,强调皮毛上不同的东西,让人产生一种“完完全全是新东西”的感觉。
如何放弃 OOP
在 FX 的 blog 中,有个<a target="_blank" href="http://rednaxelafx.iteye.com/blog/245022">小寓言</a>,大致就是讲:
<br />“object 是傻蛋的 closure, closure 是傻蛋的 object。”
<br />
<br />从 OOP 到 FP 的转变不复杂,就如 rebol 控啫喱君所说:
<br /><div class="quote_title">啫喱君 写道</div><div class="quote_div">
<br />&nbsp;&nbsp; 1. 將Object為主的程式碼轉回Procedure的方式:將Object化成Record,將Method轉成Function,將this(或self)當作Function的參數,把Function集中放到Module(模組)。
<br />
<br />&nbsp;&nbsp; 2. 將程式中用到迴圈的地方,盡量轉成遞迴(Recursion)。先不要管執行效率的問題。
<br />
<br />&nbsp;&nbsp; 3. 將程式中用到if/else或switch/case的地方,改用Pattern Matching(模式比對)。
<br />
<br />只要做到上述這三件事,你的F#程式會具有濃濃的FP風味。</div>
<br />
<br />这里的 F#,是 Haskell 的兄弟。
<br />
<br />================================================ Haskell 类型系统小小探
<br />
<br /><span style="font-size: x-large;"><span style="color: blue;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance = class X data</span></span>
<br />
<br />注意 haskell 的 class 是类型的 class,而不是对象的 class。
<br />
<br />定义 <span style="color: red;"><strong>data</strong></span> 结构(即,定义数据类型)的两种方法:
<br /><pre name="code" class="haskell">--装箱法
data Point = Point Int Int deriving Show
--使用:
Point 3 5
map (Point 1) [1..5]
--Record法
data Point = Point { x::Int, y::Int } deriving Show
--使用:
p = Point {x = 3, y = 5}
--自动产生取属性函数:
x p
y p</pre>
<br />
<br />嵌套 ok,| 也 ok。 使用 | 和嵌套定义,可以得到灵活的可变类型。
<br />deriving 语句代表它是哪些 type class 的 instance。
<br />
<br />* 从多个 type class 派生: deriving (Ord, Eq)
<br />* type 语句:别名
<br />* newtype 语句:组合了 data 和 type
<br />
<br />type <span style="color: red;"><strong>class</strong></span> 就像接口一样,定义了一些行为,如:
<br /><pre name="code" class="java">class Eq a where
(==) :: a -&gt; a -&gt; Bool
(/=) :: a -&gt; a -&gt; Bool</pre>
<br />
<br />这些行为还没有绑定到数据结构中,可以通过 <span style="color: red;"><strong>instance </strong></span>语句“实现接口”
<br /><pre name="code" class="haskell">data Chicken = Cock | Hen
instance Eq Chicken where
Cock == Cock = True
Hen == Hen = True
_ == _ = False
</pre>
<br />
<br />* 由于 Prelude 所带的 Eq 实现中,== 是用 /= 定义的, /= 是用 == 定义的, 我们只需要在 instance 中定义其中一个就行了。
<br />
<br />deriving 和 instance 作用相似,不过 deriving 的函数都定义好了。
<br />
<br />总结下来,封装、继承、绑定、多态(甚至更高阶的多态)能做的,它都能做,也不用 new 啊 new 的,很清爽呢。
<br />
<br />================================================== 长舒一口气
<br />
<br />Functor 有入选“忽悠名词小汇”的潜质。
<br />它的作用其实很简单:穿透箱子。
<br /><pre name="code" class="haskell">class Functor f where
fmap :: (a -&gt; b) -&gt; f a -&gt; f b </pre>
<br />对付 Monad 的重重包装很有用。如:
<br /><pre name="code" class="haskell">instance Functor [] where
fmap = map </pre>
<br />
<br />================================================== what's more
<br />
<br />刚得了流感,不知道是不是猪流…… 意识模糊…… 大概还能活几天的吧……
<br />
<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 一些 link |
<br />
<br />
<br />很有爱的 --&gt; learn u a haskell for great good &lt;--
<br /><a target="_blank" href="http://learnyouahaskell.com/making-our-own-types-and-typeclasses">http://learnyouahaskell.com/making-our-own-types-and-typeclasses</a>
<br />
<br />Hindley/Milner 类型系统的一篇 paper:(具体网址找不到了,G 吧……)
<br />Functional Programming with Overloading and Higher-Order Polymorphism
<br />
<br />T1 的长文 Category Theory 和 Monad:
<br /><a target="_blank" href="http://www.iteye.com/post/428151?page=1">http://www.iteye.com/post/428151?page=1</a>
<br />
<br />啫喱君的不落格 &gt;&gt;= 言程序 =&lt;&lt;:
<br /><a target="_blank" href="http://jerrylovesrebol.blogspot.com">http://jerrylovesrebol.blogspot.com</a>
<br />
Ruby 静态编程 (你看错了……)
历史:Ruby 程序员干活特别轻松,干完活后闲着没事干,就到处鼓吹,所以动静态语言大战也此起彼伏,战得不亦乐乎,很多人也从中找到了活着的意义和生命的乐趣。 各种 troll 的新方式也被发掘出来,令广大反低俗的论坛管理员头痛不已。(什么是 troll 和 troll hunter 请自行人肉)
<br />
<br />也有一些人比战斗员们(我,我不是战斗员啦!<img src="/images/smiles/icon_redface.gif" /> )更闲,把两者揉到一起,发明了 <span style="font-size: large;"><strong>DRuby</strong></span>。
<br />世界上含有“D”的词一般都说“动态”,不过别误解这个“D”,它是 —— Diamondback。
<br />
<br />昨天(5月7日), On Ruby 上发表了一个关于 Diamondback Ruby 的<a target="_blank" href="http://on-ruby.blogspot.com/2009/05/diamondback-ruby-interview.html"> &gt;&gt;= 访谈 =&lt;&lt; </a>。
<br />(相关关键字: 静态类型推导,Ocaml)
<br />
<br /><div class="quote_title">Mike Hicks 写道</div><div class="quote_div">
<br />There is a long-discussed tension between statically (or explicitly)-typed languages like Java and dynamically (or implicitly)-typed languages like Ruby. My hope has been to discover how to include the best aspects of static and dynamic typing in a single language. We all really like Ruby's design and features, and felt it was the right language to start with. As we go, we'll look to derive general design principles that make sense for most any programming language, to simplify and improve the process of programming generally.</div>
<br />
<br />
<br />两位 Mike (兄弟?)还是花了很多心血的,甚至还发了 paper:
<br /><a target="_blank" href="http://www.cs.umd.edu/projects/PL/druby/papers/druby-oops09.pdf">http://www.cs.umd.edu/projects/PL/druby/papers/druby-oops09.pdf</a>
<br />
<br />主页传送门:
<br /><a target="_blank" href="http://www.cs.umd.edu/projects/PL/druby/index.html">http://www.cs.umd.edu/projects/PL/druby/index.html</a>
<br />
<br /><div class="quote_title">Overview 写道</div><div class="quote_div">
<br />Diamondback Ruby (DRuby) is an extension to Ruby that aims to bring the benefits of static typing to Ruby without compromising the expressiveness of the language. The main features of DRuby are:
<br />
<br />&nbsp;&nbsp;&nbsp; * Type inference: DRuby uses inference to model most of Ruby’s idioms as precisely as possible without any need for programmer intervention.
<br />
<br />&nbsp;&nbsp;&nbsp; * Type annotations: Methods may be given explicit type annotations with an easy to use syntax inspired by RDoc.
<br />
<br />&nbsp;&nbsp;&nbsp; * Dynamic checking: When necessary, methods can be type checked at runtime, using contracts to isolate and properly blame any errant code, similar to gradual typing.
<br />
<br />&nbsp;&nbsp;&nbsp; * Metaprogramming support: DRuby includes a combined static and dynamic analysis to precisely model dynamic meta-programming constructs, such as eval and method_missing.
<br /></div>
<br />
<br />Quick Start 传送门:
<br /><a target="_blank" href="http://www.cs.umd.edu/projects/PL/druby/quickstart.html">http://www.cs.umd.edu/projects/PL/druby/quickstart.html</a>
<br />
<br />小小例子(通过 Annotation 指定类型):
<br /><pre name="code" class="ruby">##% quack : () -&gt; String
def quack()
"quack!"
end
</pre>
<br />
Template in Haskell
使用 template,可以做强大的事情:
<br />编译期计算、实现可变参数个数的函数、更灵活的语法……
<br />Template Haskell 可缩作 TH。
<br />
<br />用法:
<br />$(tmpfunname p1, p2, ...)
<br />
<br />做法:
<br />module XXX where
<br />
<br />import language.Haskell.TH
<br />
Some cool fonts
<img src="/upload/attachment/105201/d38cd8f3-4e59-371a-97c2-7579e28ae33f.png" />
<br />
<br /><img src="/upload/attachment/105203/44300316-6a65-3949-a758-a4bfb7376632.png" />
<br />
<br />galactic basis 外星人字体译码表:
<br />
<br /><img src="/upload/attachment/105206/19ad3507-5b27-3c03-a0e6-4e452457b339.gif" />
<br />
<br />大写字母的中心对称图形便是小写字母
<br />
<br />下面三种字体分别是 Myst,Tolkein 矮人符语 (dwarf rune),Tolkein 精灵语:
<br />
<br /><img src="/upload/attachment/105314/1ab1cccd-784e-3e1b-a613-a9fea6d12823.jpg" />
<br />
VC 编写 Ruby Native Extension Notes
<p> </p>
<p><strong><span style="color: #ff6600; font-size: medium;">VC 工程设置</span>
</strong>
</p>
<p> </p>
<p>以 1.9.1 和 msvc 10 为例<br><br><span style="color: #3366ff;">
[c/c++ | additional include directories]</span>
<br>
include 目录添加<br>
    %ruby_home%\include\ruby-1.9.1<br>
    %ruby_home%\include\ruby-1.9.1\i386-mswin32_100</p>
<p>注意不要添加 include\ruby-1.9.1\ruby,里面有个 ruby.h 重名了<br><br><span style="color: #3366ff;">
[linker -&gt; general | additional library directories]</span>
<br>
lib 目录添加<br>
    %ruby_home%\lib<br><br><span style="color: #3366ff;">
[linker | additional dependancies]</span>
<br>
链接依赖的 lib 添加<br>
    msvc100-ruby191.lib<br>
如果用到 common control,添加</p>
<p>    comctl32.lib</p>
<p><br><span style="color: #3366ff;">
[general | configuration type]</span>
<br>
输出选择<br>
    dynamic library<br><br><span style="color: #3366ff;">
[general | target extension]</span>
<br>
输出文件扩展名改成<br>
    .so<br><br><span style="color: #3366ff;">
[general | character set]</span>
<br>
字符集转换都很麻烦,按经验,最好使用</p>
<p>    multibyte charset</p>
<p> </p>
<p>另:ruby 的入口点要和输出文件名相同,大小写敏感,如 aBc.so 对应 init_aBc<br>
如果是 cpp,入口点添加 extern "C" 修饰符。示例:</p>
<pre name="code" class="cpp">extern "C" void __declspec(dllexport) Init_aBc();</pre>
 
<p> </p>
<p> </p>
<p><strong><span style="color: #ff6600; font-size: medium;">建立窗口句柄 和 ruby object 之间的对应关系</span>
</strong>
</p>
<p> </p>
<p>用到的 API: <span style="color: #3366ff;"><strong><span>SetWindowLong </span>
</strong>
</span>
</p>
<p>windows 窗体有一个类型为 long 的 slot 可以存 user data,ruby obj 在 C 里面的类型是 VALUE (unsigned long)。</p>
<p><span style="color: #808080;"><em><span>关于 VALUE 如何对应 ruby 的各种类型,可参考 ruby hacking guide 或看 MRI 源代码。</span>
</em>
</span>
</p>
<p>虽然 unsigned long 和 long 有些区别,但只要 obj 不是直接量,VALUE 就对应其内存地址,而 32 位有符号整数,只用非负部分,寻址空间有 512G,不能表示的位置几乎不可能达到,所以可以放心互转并存入窗体结构中。</p>
<p> </p>
<p>将窗口句柄 h 和 ruby obj 对应起来,可以:</p>
<pre name="code" class="cpp">SetWindowLong(h, GWL_USERDATA, (long)obj);</pre>
 
<p>handle 同时也是一个整数,所以可以存到 obj 的属性中,假如存入 obj.h</p>
<pre name="code" class="cpp">rb_funcall(obj, rb_intern("h="), 1, LONG2NUM((long)h));</pre>
 
<p>存过以后,可以定义 handle 和 object 相互转换的宏</p>
<p> </p>
<pre name="code" class="cpp">inline VALUE HWND2VALUE(HWND h) {
long l = GetWindowLong(h, GWL_USERDATA);
return l ? (VALUE)l : Qnil;
}
inline HWND VALUE2HWND(VALUE v) {
VALUE res = rb_funcall(v, ID_h, 0);
return (res == Qnil) ? 0 : (HWND)NUM2LONG(res);
}</pre>
<p>ps:后来还是改用了一个中间结构。setwindowlong 存了个指针。</p>
<p> </p>
<p> </p>
<p><span style="color: #ff6600;"><strong><span style="font-size: medium;">用 ruby proc 进行消息处理</span>
</strong>
<br></span>
</p>
<p> </p>
<p>signal / slot 的方式很流行,但缺点是很慢,ruby 本来就很慢,这里再慢就更难受了。</p>
<p>我觉得用类 MFC 的消息映射宏比较好(实际上还要处理一些细节,不过无关主题,就省去了):</p>
<p> </p>
<pre name="code" class="cpp">#include &lt;map&gt;
// STL 的 map 内建红黑树. key 为窗口句柄, value 为 ruby proc
typedef std::map&lt;HWND, VALUE&gt; MAP;
#define BEGIN_MSG \
WNDPROC _old_proc = 0;\
LRESULT CALLBACK _new_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {\
switch(message) {
// connect msg and map : 通过句柄查找 proc,如果找到,就调用处理函数
#define CONNECT(msg, map) \
case msg:\
do{\
MAP::iterator v = map.find(hwnd);\
if(v != map.end()) {\
rb_funcall(v-&gt;second, rb_intern("call"), 1, HWND2VALUE(hwnd));\
return 0;\
}\
}while(0);\
break;
#define END_MSG \
}\
return CallWindowProc(_old_proc, hwnd, message, wparam, lparam);\
}</pre>
 
<p>一个定义 绑定左键点击的函数 (click_eq) 例子:</p>
<p> </p>
<pre name="code" class="cpp">namespace{
// 每消息一个 map
MAP map; // =Map();
BEGIN_MSG;
CONNECT(WM_LBUTTONDOWN, map);
END_MSG;
VALUE click_eq(VALUE self, VALUE proc) {
if(proc != Qnil)
map[VALUE2HWND(self)] = proc;
return proc;
}
... // 定义创建窗口等其它函数
}
... // 把 click_eq 绑定到 click= 方法</pre>
 
<p>ruby 的用法就像这样:</p>
<p> </p>
<pre name="code" class="ruby">obj.click= proc{|this| puts this}
</pre>
<p> </p>
<p> </p>
<p><strong><span style="color: #ff6600; font-size: medium;">装载外部字体</span>
</strong>
</p>
<p> </p>
<p>从 why 的 shoes 源代码中看到,超简单:</p>
<pre name="code" class="cpp">AddFontResource(StringValueCStr(path_str));</pre>
<p> </p>
<p> </p>
<p><strong><span style="color: #ff6600; font-size: medium;">win xp 风格的窗体控件</span>
</strong>
</p>
<p> </p>
<p>首先需要 common control。包括 #include &lt;commctrl.h&gt; 和 comctl32.lib</p>
<p>保证 common control 的 dll 加载:</p>
<pre name="code" class="cpp">INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_TAB_CLASSES | ICC_BAR_CLASSES | ICC_STANDARD_CLASSES | ICC_USEREX_CLASSES;
InitCommonControlsEx(&amp;icex);
</pre>
 
<p> </p>
<p>这一行是必须的,摆在 stdafx.h 里头,#pragma once 下面。</p>
<pre name="code" class="cpp">#define ISOLATION_AWARE_ENABLED 1</pre>
<p> </p>
<p><span style="color: #0000ff;">[manifest tool -&gt; input and output | Additional manifest files]</span>
</p>
<p>添加</p>
<p>    ext.dll.manifest</p>
<p> </p>
<p>内容如下</p>
<pre name="code" class="xml">&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
&lt;assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"&gt;
&lt;assemblyIdentity version="1.0.0.0" processorArchitecture="X86"
name="Ruby.Cici.ext" type="win32" /&gt;
&lt;description&gt;ext.so&lt;/description&gt;
&lt;dependency&gt;
&lt;dependentAssembly&gt;
&lt;assemblyIdentity type="win32"
name="Microsoft.Windows.Common-Controls" version="6.0.0.0"
processorArchitecture="X86" publicKeyToken="6595b64144ccf1df"
language="*" /&gt;
&lt;/dependentAssembly&gt;
&lt;/dependency&gt;
&lt;/assembly&gt;
</pre>
 
<p> </p>
<p><strong><span style="color: #ff6600; font-size: medium;">block 相关 api </span>
</strong>
</p>
<p> </p>
<p><span style="color: #0000ff;">rb_yield(VALUE)</span>
最简单,相当于单参数 yield</p>
<p><span style="color: #0000ff;">rb_yield_values(int, ...)</span>
可以指定 yield 参数个数</p>
<p><span style="color: #0000ff;">rb_block_proc()</span>
可以获得当前的 block proc 对象</p>
<p><span style="color: #0000ff;">rb_block_given_p()</span>
测试是否给了 block</p>
<p> </p>
<p><span style="color: #0000ff;">rb_block_call</span>
和<span style="color: #0000ff;"> </span>
<span style="color: #0000ff;">rb_iterate</span>
<span style="color: #0000ff;"> </span>
最变态,函数签名:</p>
<pre name="code" class="cpp">VALUE rb_iterate(VALUE(*)(VALUE),VALUE,VALUE(*)(ANYARGS),VALUE);
VALUE rb_block_call(VALUE,ID,int,VALUE*,VALUE(*)(ANYARGS),VALUE);</pre>
<p> </p>
<p>感悟:每种语言的设计者,都对某种符号或者某些词有特殊的爱…… </p>
<p> </p>
<p> </p>
<p><strong><span style="color: #ff6600; font-size: medium;">关于 font</span>
</strong>
</p>
<p> </p>
<pre name="code" class="cpp">VALUE set_default_font(VALUE self, VALUE font_str, VALUE char_set, VALUE font_sz) {
ghFont = CreateFont(-NUM2INT(font_sz),0,0,0,0,0,0,0,
NUM2INT(char_set), OUT_DEVICE_PRECIS, CLIP_DEFAULT_PRECIS,
PROOF_QUALITY, FF_DONTCARE, StringValueCStr(font_str));
return self;
}
...
SendMessage(hButtonStart, WM_SETFONT, ghFont, 0);</pre>
<p>注意字体要和 charset 相搭配,否则不干活。打 gb2312 alt+右 f12 查看 charset 定义。</p>
<p> </p>
<p> </p>
<p>工作成果: <a href="http://code.google.com/p/ruby191ext/downloads/list" title="http://code.google.com/p/ruby191ext/downloads/list">http://code.google.com/p/ruby191ext/downloads/list</a>
</p>
预处理杂碎(附 vc10 的 c++0x 特性小小结)
<p>Ruby 的 mixin 原来是从这里学来的……</p>
<pre name="code" class="cpp">class A {
#include "a_module.cpp"
}</pre>
<p> </p>
<p>总觉得 std::for_each 非常的不爽,90% 情况写 begin() 和 end() 都是多余的。利用 c++0x 的<strong> <span style="color: #008000;">auto </span>
</strong>
关键字:</p>
<pre name="code" class="cpp">#define each_do(c, i) \
for(auto i = c.begin(), what##i##ever = c.end(); i != what##i##ever; ++i)
#define each_rdo(c, i) \
for(auto i = c.rbegin(), what##i##ever = c.rend(); i != what##i##ever; ++i)</pre>
<p> </p>
<p>用完就丢的 macro:(保护键盘,保护剪贴板!)</p>
<pre name="code" class="cpp">#define def(method, func, pcount) rb_define_method(klass, method, RUBY_METHOD_FUNC(func), pcount)
def("create",create,2);
def("destroy",destroy,0);
def("width",width,0);
def("height",height,0);
#undef def</pre>
<p> </p>
<p>用完就丢的 macro 扩大版:(把 #define 和 #undef 分别放到两个头文件里)</p>
<pre name="code" class="cpp">#include "define_macros.h"
... // bla bla 干活
#include "undef_macros.h"</pre>
<p> </p>
<p>没有 lambda 时,一个 block 结构不能放到括号里,如果宏要产生一个值,用了多行语句你就不能括起它,只能改成 inline function,但是 inline function 没有宏这么自由,还得仔细考虑参数类型、传值还是传引用…… 如果用到 type 作为参数,就得用 template 了…… 总之严重影响思考效率。</p>
<p>有了 <strong><span style="color: #008000;">lambda</span>
</strong>
后, 事情改观鸟~</p>
<p> </p>
<p>从 a 加到 b:(改成 FX 给的版本了)</p>
<pre name="code" class="cpp">#define sum_range(a, b) \
([]() -&gt; decltype(b) { \
auto ta = (a); auto tb = (b); auto s = tb; \
for(auto i = ta; i &lt; tb; ++i) { s += i; }; return s;}())</pre>
<p> </p>
<p>注1:C++ 毕竟不是类型推断的高手,如果 s 被声明为 auto,<strong><span style="color: #008000;">decltype</span>
</strong>
(s) 就变成 void 了,还是得指定返回类型。</p>
<p>注2:decltype(i) 为 int 的时候, decltype((i)) 为 int&amp;, 原因:左值和右值区别对待。</p>
<p>注3:方括号(lambda-introducer)总结</p>
<p> </p>
<p><span style=""><span style="color: #0000ff;">[=]</span>
    </span>
默认按值捕获</p>
<p><span style=""><span style="color: #0000ff;">[&amp;] </span>
   </span>
默认按引用捕获</p>
<p><span style=""><span style="color: #0000ff;">[] </span>
    </span>
不捕获(无closure)</p>
<p><span style="color: #0000ff;">[=,&amp;a,&amp;b]</span>
默认按值,a 和 b 按引用</p>
<p><span style=""><span style="color: #0000ff;">[a] </span>
   </span>
只按值捕获 a</p>
<p><span style=""><span style="color: #0000ff;">[this]  </span>
</span>
只捕获 this(没有 &amp;this)</p>
<p> </p>
<p>一个用 lambda 的 template :</p>
<pre name="code" class="cpp">template &lt;class Func&gt;
class A {
public:
A(Func f){f(12);}
};
int main(int argc, char* argv[])
{
// #include &lt;functional&gt; 后, f 可以声明为 std::function&lt;void(int)&gt;
// 但只有 decltype(f) 能传给 template
auto f = [](int i){
printf("%d from a lambda\n", i);
};
A&lt;decltype(f)&gt; a(f);
return 0;
}</pre>
<p> </p>
<p> </p>
<p>----------------------------- <a href="http://en.wikipedia.org/wiki/C%2B%2B0x" title="http://en.wikipedia.org/wiki/C%2B%2B0x">http://en.wikipedia.org/wiki/C%2B%2B0x</a>
----------------------------</p>
<p> </p>
<p>在 vc10 尝试检验了一些 c++0x 特性。</p>
<p> </p>
<p>range based for-loop<span style="color: #ff0000;">(不支持)</span>
</p>
<pre name="code" class="cpp">int my_array[5] = {1, 2, 3, 4, 5};
for(int &amp;x : my_array)
{
x *= 2;
}</pre>
 
<p>initializer list <span style="color: #ff0000;">(不支持)</span>
</p>
<pre name="code" class="cpp">std::vector&lt;std::string&gt; v1{"he","llo"};
std::vector&lt;int&gt; v2 = {1,2,3,4};</pre>
 
<p>static_assert 编译期检查。类似契约。<span style="color: #008000;">(支持)</span>
</p>
<pre name="code" class="cpp">static_assert( constant-expression, error-message ) ;</pre>
 
<p>改进字符串<span style="color: #ff0000;">(不支持)</span>
</p>
<pre name="code" class="cpp">u8"I'm a UTF-8 string.\u2018" //const char[]
u"This is a UTF-16 string." //const char16_t[]
U"This is a UTF-32 string." //const char32_t[]
R"[The String Data \ Stuff " ]" // "[ 和 ]" 中间的 " 和 \ 都不转义
u8R"EOS[I'm a "raw UTF-8" string.]EOS" //here doc: EOS[ 开始, ]EOS 结束</pre>
 
<p>用户定义 literal(通过字符串后缀产生数据)<span style="color: #ff0000;">(不支持)</span>
</p>
<pre name="code" class="cpp">OutputType operator "" _Suffix(const char *literal_string); //返回 OutputType 类型的数据
OutputType someVariable = "1234"_Suffix;</pre>
<p> </p>
<p>regexp<span style="color: #008000;">(支持)</span>
</p>
<pre name="code" class="cpp">#include &lt;regexp&gt;
...
std::regexp r("[a\\s]");</pre>
<p> </p>
<p>其它特性: hash<span style="color: #008000;">(支持)</span>
,rvalue-reference<span style="color: #008000;">(支持)</span>
</p>
[Cici] 正则表达式小工具
折腾了好久,终于把 tab 的问题搞定了……
<br />api 创建的非对话框窗口根本就没封装 tab focus 的能力,必须得自己处理 tab key press,被一个 WS_TABSTOP 误导了<img src="/images/smiles/icon_evil.gif" />
<br />
<br />顺便出笼一个简陋的正则表达式小工具:
<br />
<br /><img src="/upload/attachment/111889/fb034a08-1d0d-3ee4-b909-1dd3383f11a3.png" />
<br />
<br />
<br />如果开了 theme,会好看一点&nbsp;<img src="/images/smiles/icon_wink.gif" />
<br />
<br /><pre name="code" class="ruby">
require 'cici.rb'
include Cici::View
Cici::Config &lt;&lt; {
:default_font =&gt; ['Courier New', 12, '', 0],
}
@check_reg = -&gt; this do
begin
re = Regexp.new @reg.text
match = (re =~ @txt.text)
@result.text = (match ? match.to_s : 'nil')
@globals.each_with_index do |g, i|
g.text = (eval "$#{i+1}") || 'nil'
end
rescue Exception =&gt; ex
@result.text = ex.message
@globals.each do |g|
g.text = 'nil'
end
end
end
view 'Regular Expression Test', [390, 245] do |v|
v.bkcolor = 0xffbb99 # windows 的 RGB 是倒置的…… 得改
v.marginx = 8
br 10
sz1 = [280, 20]
text 'regex: /'
@reg = edit sz1
@reg.ontab = -&gt; this { @txt.focus; @check_reg[nil] }
# 按下 tab 键时,转移焦点并触发匹配检查
text '/'
br
text 'string: '
@txt = edit sz1
@txt.ontab = -&gt; this { @reg.focus; @check_reg[nil] }
br
text 'result: '
@result = edit sz1
br 10
paint [280, 115] do
fmonaco = Cici::Font.new 'Monaco', 12
@globals = (1..9).map do |i|
text " $#{i}", :font =&gt; fmonaco # 这里也可以用 ['Monaco',12],不过会产生 9 个字体句柄。
e = edit [50, 20]
br if (i==3 or i==6)
e
end
end
paint [60, 115] do
sz = [60, 22]
button 'match', sz, &amp;@check_reg
br 8
button 'copy', sz, do
@reg.copy
alert "\"#{@reg.text}\" copied."
end
br 8
button 'escape', sz, do
@reg.text = Regexp.escape @reg.text
end
end
@reg.focus
end
</pre>
<br />
<br />依赖关系…… 得打个包
<br /><a target="_blank" href="http://code.google.com/p/ruby191ext/downloads/list">http://code.google.com/p/ruby191ext/downloads/list</a>
Wrapping Data in Ruby Objects
<span style="font-size: large;"><span style="color: orange;"><strong>目的:建立 ruby 对象和 C 结构之间的关系</strong></span></span>
<br />
<br />一种方法是用一个实例变量来存储结构对象的指针。例如:
<br /><pre name="code" class="java">rb_iv_set(rb_obj, "@ptr", LONG2NUM((long)ptr))</pre>
<br />问题在于 @ptr 是 ruby 可以访问的,不知内情的人如果碰了它,就很容易出来一个 segmentfault(非法内存地址)。
<br />而且指针在 32 位的系统里是 long,但在 8 字节系统里却是 long long,得把 LONG2NUM 改成 ll2NUM。
<br />
<br />另一种方法是用 ruby 提供的<span style="color: blue;"> Data_Wrap_Struct</span> 宏。
<br />Data_Wrap_Struct 在 ruby 里 new 出一个对象,这个对象隐式捆绑一个 C 结构的指针……所谓隐式,就是 ruby 中无法访问,让人安心……
<br />
<br /><div class="quote_title">Ruby V1.9 C Extension 写道</div><div class="quote_div">
<br />Encapsulate C data into Ruby object
<br />
<br />To wrap and objectify a C pointer as a Ruby object (so called DATA), use Data_Wrap_Struct().
<br />
<br /> <pre name="code" class="c">Data_Wrap_Struct(klass, mark, free, ptr)</pre>
<br />
<br />Data_Wrap_Struct() returns a created DATA object. The klass argument is the class for the DATA object. The mark argument is the function to mark Ruby objects pointed by this data. The free argument is the function to free the pointer allocation. If this is -1, the pointer will be just freed. The functions mark and free will be called from garbage collector.
<br />
<br />You can allocate and wrap the structure in one step.
<br />
<br /> <pre name="code" class="c">Data_Make_Struct(klass, type, mark, free, sval)</pre>
<br />
<br />This macro returns an allocated Data object, wrapping the pointer to the structure, which is also allocated. This macro works like:
<br />
<br /> <pre name="code" class="c">(sval = ALLOC(type), Data_Wrap_Struct(klass, mark, free, sval))</pre>
<br />
<br />Arguments klass, mark, and free work like their counterparts in Data_Wrap_Struct(). A pointer to the allocated structure will be assigned to sval, which should be a pointer of the type specified.
<br />
<br />To retrieve the C pointer from the Data object, use the macro Data_Get_Struct().
<br />
<br /> <pre name="code" class="c">Data_Get_Struct(obj, type, sval)</pre>
<br />
<br />A pointer to the structure will be assigned to the variable sval.
<br /></div>
<br /><span style="color: violet;"><strong>补充解释一些参数:</strong></span>
<br /><strong><span style="color: blue;">klass</span></strong>:对应的 ruby 类(C 内类型是 VALUE)。
<br /><strong><span style="color: blue;">mark</span></strong> :标记函数,一般用来让这个对象不会被 gc 扫除掉。典型写法是
<br /><pre name="code" class="c">void markf(cstruct_type *ptr) { rb_gc_mark(ptr-&gt;related_val);}</pre>
<br /><strong><span style="color: blue;">sval</span></strong> :对应的 C 结构 (cstruct_type) 指针。sval 是设值型参数。
<br /><strong><span style="color: blue;">free</span></strong> : gc 删除该对象时的回调函数,典型写法是
<br /><pre name="code" class="c">void freef(cstruct_type *ptr) { delete ptr;}</pre>
<br />
<br />
<br /><span style="font-size: large;"><span style="color: orange;"><strong>Speed Issues</strong></span></span>
<br />
<br />rb_gc_mark 包含一次内存分配(你干嘛还 alloc 啊……),也是非常慢的操作。
<br />
<br />提升 rb_gc_mark 的速度,可以用一个已 mark 的数组或者 hash 保存对象,只要有引用指向了这个对象,就会添加一个 mark,而这种天然 mark 过程不包含 alloc 操作……
<br /><div class="quote_title">ruby embedded into c++ 写道</div><div class="quote_div">
<br /><pre name="code" class="c++">
class Objects {
private:
VALUE objects;
public:
Objects() {
objects = rb_ary_new();
rb_gc_register_address(&amp;objects);
}
~Objects() {
// dispose array and flush all elements
rb_gc_unregister_address(&amp;objects);
/*
mass destruction. GC can no longer
mark the elements in the Array and
therefore they will all get swept.
*/
}
void Register(VALUE object) {
rb_ary_push(objects, object);
}
void Unregister(VALUE object) {
rb_ary_delete(objects, object);
}
};
</pre></div>
<br />
<br />同样由于每次调用都会分配内存,应该尽量避免 Data_Make_Struct。
<br />
<br /><strong><span style="color: violet;">提速方法A: </span></strong>
<br />使用局部变量或者用 ALLOCA / ALLOCA_N 在栈内分配 struct 的内存,出栈的时候自然会 free 掉。栈内存分配比 ALLOC、malloc 和默认 new 运算符快多了。
<br />
<br /><strong><span style="color: violet;">提速方法B:</span></strong>
<br />自定义该结构的 Allocator 或者 new 操作符,一次分配一大块内存,用的时候按格子分。(类似于 Pool Allocator)。对小型、定长、大量对象非常有效。
[Cici] 一个咸咸淡淡的菜单
哦不,简简单单的菜单。
<br /><span style="color: gray;">写过 wow 插件的朋友会发现它比较像 Ace2 Lib 的 <a target="_blank" href="http://www.wowace.com/projects/waterfall-1-0/">waterfall</a> 菜单。</span>
<br />
<br />一张图差不多了……
<br />
<br /><img src="/upload/attachment/112187/181a508a-e87b-32e3-90c3-9d5712eec94c.png" />
<br />
<br />
<br /><span style="color: #3399ff;"><strong>关于 Ruby 1.9 的一些语法:</strong></span>
<br />
<br />** Hash 中的 a:b 相当于 :a =&gt; b, 但是要注意如果用非符号做 key,就不能这样写,还得用 =&gt; 连接
<br />
<br />** Proc#curry 将多参数的 Proc 变成多次<span style="color: red;">任意</span>参数的 Proc,只要参数给不完,它就返回 Proc 而不运行。
<br /><span style="color: gray;">如果 proc1 的调用方式是 proc1[a, c],那么 proc1.curry 可以这样调用: proc1[a][c](即 proc1[a] 返回一个新的 proc)</span>
<br />
<br /><span style="color: #3399ff;"><strong>另外:</strong></span>
<br />
<br />** line: nil 插入一个分隔符
<br />
<br />** 1.9 的有序 Hash 很重要
<br />
<br />** 同级菜单不可以有相同 key,异级菜单可以 █
<br />
<br />
My Github Repos
<pre name="code" class="">---------------------------------------------------------------------</pre>
<br /><span style="font-size: large;"><strong><span style="color: indigo;">ps-1</span></strong></span>
<br /><a target="_blank" href="http://github.com/luikore/ps-1/tree/master">http://github.com/luikore/ps-1/tree/master</a>
<br />
<br />极其简单的 windows 命令行增强。
<br />将 bin 和 ruby 安装目录的 bin 添加到环境变量 path 中即可。
<br />
<br /><pre name="code" class="">---------------------------------------------------------------------</pre>
<br /><span style="font-size: large;"><strong><span style="color: indigo;">iconv-pure</span></strong></span>
<br /><a target="_blank" href="http://github.com/luikore/iconv-pure/tree/master">http://github.com/luikore/iconv-pure/tree/master</a>
<br />
<br />ruby 1.9 已经有 String#encoding 了,但是还有些库依赖于 iconv (像 activesupport)。
<br />而编译 iconv ext 有些困难(尤其是在 windows 上面),所以做了一个 iconv 的简单替代品,不需要编译 iconv 也能使用 Iconv 类的大部功能。
<br />(真的很小 …… 只有不到 100 行的 ruby)
<br />
<br />安装:
<br /><pre name="code" class="ruby">gem install luikore-iconv-pure</pre>
<br />
<br />API 基本和 Iconv 一样。
<br />
<br /><pre name="code" class="">---------------------------------------------------------------------</pre>
<br /><span style="font-size: large;"><strong><span style="color: indigo;">bk201</span></strong></span>
<br /><a target="_blank" href="http://github.com/luikore/bk201/tree/master">http://github.com/luikore/bk201/tree/master</a>
<br />
<br />在 ruby 中直接写机器码。和 <a target="_blank" href="http://rednaxelafx.iteye.com/">FX</a> 做实验的工具 ……
<br />有时比 C 程序跑得快也是没办法的 ╮(╯▽╰)╭
<br />安装:
<br />需要懂些汇编。x86 机器,win32,VC。
<br />
<br />相关链接:
<br /><a target="_blank" href="http://night-stalker.iteye.com/blogs/430165">http://night-stalker.iteye.com/blogs/430165</a>
<br />
<br />ps:wilson 的完成度比这个高,而且是纯 ruby 实现的不用编译一趟,参见
<br /><a target="_blank" href="http://rednaxelafx.iteye.com/blog/471747">http://rednaxelafx.iteye.com/blog/471747</a>
<br />
<br /><pre name="code" class="">---------------------------------------------------------------------</pre>
<br /><span style="font-size: large;"><strong><span style="color: indigo;">cici</span></strong></span>
<br /><a target="_blank" href="http://github.com/luikore/cici/tree/master">http://github.com/luikore/cici/tree/master</a>
<br />
<br /><img src="/upload/attachment/113877/b33f4881-65f2-3b2d-bb8f-9e9ec79b5301.png" />
<br />
<br />超微型的 Ruby GUI 小工具,<strong> Windows only</strong>,写起来也超短。
<br />界面以文本为主,集成 scintilla edit 2.00 (notepad++,komodo,scite,code::blocks 都用 scintilla 哟)
<br />
<br /><span style="color: #3388ff;"><strong>安装</strong></span>:首先需要 ruby-1.9.1-mswin32_90 或者 ruby-1.9.1-mingw32,二者都可以在 download 页面下载:
<br /><a target="_blank" href="http://github.com/luikore/cici/downloads">http://github.com/luikore/cici/downloads</a>
<br />
<br />或者从 rubyinstaller 那儿下一个 mingw32 版本也不错:
<br /><a target="_blank" href="http://rubyinstaller.org/downloads/">http://rubyinstaller.org/downloads/</a>
<br />
<br />然后:
<br /><pre name="code" class="console">gem install luikore-cici --source=http://gems.github.com</pre>
<br />
<br /><span style="color: #3388ff;"><strong>协议</strong></span>:MIT
<br />‭
<br /><span style="color: #3388ff;"><strong>状态</strong></span>:<strong>刚开始</strong>。C++ 代码大约 4k 行。
<br />
<br /><span style="color: #3388ff;"><strong>参考</strong></span>:Shoes,FXRuby,Ruby/GTK,REBOL,HTML/Js,FLTK,wxHaskell,WTL,Pratical Ruby,Visual Basic(小时候超喜欢这个) 以及 Code Project 上面的一些 GUI 相关文章。
<br />
<br /><span style="color: #3388ff;"><strong>读作</strong></span>:<strong>C C</strong>
<br />
[Cici] 灵巧秒表
小小的计时器:
<br /><img src="/upload/attachment/112378/ad9d0ccb-1eb1-3074-8024-11a501e09b55.png" />
<br />
<br />主要是添加了这两个函数(分别对应 win32 api 的 SetTimer 和 KillTimer):
<br /><span style="color: blue;"><strong>set_timer(milisec, &amp;block)</strong></span>&nbsp; 返回 timer id
<br /><span style="color: blue;"><strong>kill_timer(tid)&nbsp; </strong></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 杀死指定 id 的 timer
<br />
<br />另外 ruby 有个方便的函数:
<br /><span style="color: blue;"><strong>String#succ</strong></span>
<br />
<br /><pre name="code" class="ruby">
require 'cici.rb'
include Cici::View
# 计时器 - 秒
module Second
attr_writer :carry_target # 进位的目标
def incr
new_text = self.text.succ
if new_text == '60.0' # 满 60 便进位
self.text = '0.0'
@carry_target.text = @carry_target.text.succ
else
self.text = new_text
end
end
def stop
(self.kill_timer @tid) if @tid
end
def start
@tid = self.set_timer 95 do |this| # 95 毫秒为间隔
this.incr
end
end
end
# 界面 helper
@timer_font = Cici::Font.new 'Arial', 14
# time text
def ttext txt
text txt, :font =&gt; @timer_font
end
# 界面
view 'Timer', [230, 63] do |box|
br
box.bkcolor = 0x88ffff
@min = ttext '0'
ttext 'min'
@sec = ttext '0.0'
@sec.__send__ :extend, Second # 添加行为
@sec.carry_target = @min
ttext 'sec'
# 下面是两个按钮,start/pause 和 reset
@start = -&gt;this {
@sec.start
this.text = 'pause'
this.onclick = @pause
}
@pause = -&gt;this {
@sec.stop
this.text = 'start'
this.onclick = @start
}
@sbutton = button 'start', &amp;@start
button 'reset' do
@pause[@sbutton]
@sec.text = '0.0'
@min.text = '0'
end
end
</pre>
<br />
<br />ps:暂时不太准,标准的方法是每隔一段时间通过系统时间校正 timer。
<br />
[Cici] 图片显示 Notes
<img src="/upload/attachment/112741/c6b3ad85-c232-3468-b301-bad2a098c975.png" />
<br />
<br />以下为实现过程小记。
<br />
<br />Windows 下使用 <a target="_blank" href="http://msdn.microsoft.com/en-us/library/bwea7by5(VS.80).aspx">ATL CImage</a> 就足够了,轻小又不需要额外的运行时(编译后的代码只增加了 4k 左右),支持 bmp,jpg,gif,png 等格式,比千奇百怪的第三方库好很多……
<br />
<br />首先需要头文件:
<br /><pre name="code" class="c++">
#pragma warning(disable: 4005) // 关闭 4005 警告 (macro redefinition)
#include &lt;atlimage.h&gt;
// 其它 include
#include "ruby.h"
...
</pre>
<br />
<br />要注意:
<br />** &lt;atlimage.h&gt; 要比 ruby.h 早 include,否则会编译出错。
<br />** 编译参数 #pragma warning(disable: 4005) 是因为: ruby.h 重定义了很多 errno,会引起一堆警告。
<br />
<br />载入图像:
<br /><pre name="code" class="c++">
auto img = new ATL::CImage();
img-&gt;Load("quantummi7.jpg");
// 也可以 Load 一个 Stream,适合预处理或者使用 internet 上的图形
</pre>
<br />
<br />然后在处理 WM_PAINT 消息时画图:
<br /><pre name="code" class="c++">
PAINTSTRUCT ps;
HDC dc = BeginPaint(hwnd, &amp;ps);
img-&gt;Draw(dc, 0, 0); // 从 hwnd 的 Client 左上角画起
EndPaint(hwnd, &amp;ps);
</pre>
<br />
<br />一些其它功能:
<br /><pre name="code" class="c++">
img-&gt;Width()
img-&gt;Height()
</pre>
<br />
<br />
<br />
Colony (无内容)
<p>from 2chan。日升的作画肯定是 NASA 每日一图的粉丝。</p>
<p> </p>
<p>link back:</p>
<p><a href="/blog/349271" title="http://night-stalker.iteye.com/blog/349271">http://night-stalker.iteye.com/blog/349271</a></p>
<p><br><img src="/upload/attachment/113060/ffb10200-e019-30ee-ab4e-656dcb3b038a.jpg" alt=""></p>
<p>     <br><img src="/upload/attachment/113062/944c052c-da14-323c-9d7b-ed557fae06f0.jpg" alt=""></p>
<p> </p>
<p> </p>
windows 下 ruby extension dll hell 问题的终极解决方案~
<span style="color: blue;">什么是 dll hell</span>: <a target="_blank" href="http://en.wikipedia.org/wiki/Dll_hell">http://en.wikipedia.org/wiki/Dll_hell</a>
<br /><span style="color: blue;">为什么 Ruby 自身不管这个问题</span>: 因为 ruby core team 不用 windows,并且对 manifest 存在误解(他们甚至还脑残的说这是 from dll hell to manifest hell)
<br /><span style="color: blue;">为什么没有人贡献解决这个问题的补丁</span>:因为官方 binary 先是用 VC6 编译,1.9 后倒退到 mingw,这两个东西都不包含 manifest tool ……
<br />
<br />解决问题的关键: Manifest。
<br />
<br />下面以 ruby 1.8.6 (官方 binary) ,本地安装 json-1.1.6.gem (不带 mswin32 后缀的需要编译) 和 VC 2008 Express 作演示。
<br />
<br />1.8.6 官方 binary 是 VC 6 编译的,它不允许其它版本的 VC 编译器,解决方法是打开 lib\ruby\1.8\i386-mswin32\config.h 注释掉第二行。(1.9 以后就没这个限制了)
<br /><pre name="code" class="c">#if _MSC_VER != 1200
//#error MSC version unmatch
#endif</pre>
<br />
<br />打开 Visual Studio Command Prompt,修改环境变量添加 ruby_home 的 include 目录和 lib 目录,然后 gem install json-1.1.6.gem, 其间会见到“building native extensions”,就是编译器在干活了。
<br />
<br />安装完后别急 —— gem 和 ruby 的编译器不同,所以运行时不同,用起来必然会出错。M$ 为了解决这个问题,借用了 .NET 的方式,使用 <span style="color: blue;"><strong>manifest</strong></span> 来决定应该加载的 dll 运行时。
<br />
<br />转到目录 %ruby_home%\lib\ruby\gems\1.8\gems\json-1.1.6\ext\json\ext 下面,看见两个 so 文件吧。它们是改了名的 dll 文件。还有两个目录: generator 和 parser,分别包含这两个动态链接库的源文件。现在得用 mt (manifest tool)对它们进行修正,使它们可以装载正确的运行时:
<br />
<br />转到 parser 目录,<span style="color: brown;"><strong>嵌入 manifest</strong></span>:
<br /><pre name="code" class="console">mt -manifest parser.so.manifest -outputresource:parser.so;2</pre>
<br />
<br /><span style="color: brown;"><strong>然后将外面那个 parser.so 替换掉</strong></span>:
<br /><pre name="code" class="console">nmake install</pre>
<br />
<br />同样的事情,对 generator.so 做一遍。
<br />
<br />最后打开 irb, require 'json';JSON.load '[1,2]',工作正常~
<br />
<br />经典老文(内含大量不雅词汇,打开前请关闭格林达姆等软件):
<br /><a target="_blank" href="http://apocryph.org/2007/06/16/totally_bullshit_ruby_extension_experience_windows/">http://apocryph.org/2007/06/16/totally_bullshit_ruby_extension_experience_windows/</a>
<br />
<br />mt.exe
<br /><a target="_blank" href="http://msdn.microsoft.com/en-us/library/aa375649.aspx">http://msdn.microsoft.com/en-us/library/aa375649.aspx</a>
<br />
<br />mt.exe 2
<br /><u>控制台输入 mt /? | MORE</u>
<br />
<br /><span style="font-size: x-large;"><strong><span style="color: #775500;"> 结论: 你不需要出土文物 VC6,就能安装一些需要本地编译的 gem </span></strong></span>
<br />
<br />
有用的无用项 + 肯定的否定项
观 TED(Technology, Entertainment, Design) 有感。
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">有用的无用项</span></strong></span>
<br />
<br /><strong>第一项</strong>. A服务 $68
<br /><strong>第二项</strong>. B服务 $125
<br /><strong>第三项</strong>. B服务附送礼品 $125
<br />
<br />当第二项存在时,没人选第二项,但大部分人都选第三项。
<br />但如果去掉第二项,大部分人都选第一项。
<br />
<br />奸商推销积压商品时,常常会添加一个怎么看都比积压品弱的对照组。
<br />表单设计也是如此,添加一个极其傻叉的对照组,可以诱导别人去点傻叉的选项 ……
<br />
<br />通过视觉混淆骗点击的链接,不如这种“对照法”高明,至少人家点了不会怪你。
<br />
<br />
<br /><span style="font-size: large;"><strong><span style="color: green;">肯定的否定项</span></strong></span>
<br />
<br /><strong>表单一</strong>:
<br />打钩 == 你死后自动捐赠器官
<br />
<br /><strong>表单二</strong>:
<br />打钩 == 你死后不自动捐赠器官
<br />
<br />人们都懒得打钩,而且多数脑子遇到否定项都会秀逗掉。用表单二的国家获得几乎 100% 的器官捐赠率,而用表单一的国家很少人捐器官。
<br />
<br />一些软件装完后还提示一些安装其流氓插件的选项,并给你全部钩选 —— 噢不,人们还是会把钩钩一个个去掉的。更有效的做法是默认不打钩,并且把表单改成:
<br />
<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>打钩 == 不安装我们的流氓插件 </strong>
<br />
Know Ruby Standard Library
熟悉 API 是个体力活…… 为了避免重复劳动,过一遍还是需要的。
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">FileUtils</span></strong></span>
<br />
<br /><pre name="code" class="ruby">
require 'fileutils'
include FileUtils
cd(dir, options)
cd(dir, options) {|dir| .... }
pwd()
mkdir(dir, options)
mkdir(list, options)
mkdir_p(dir, options)
mkdir_p(list, options)
rmdir(dir, options)
rmdir(list, options)
ln(old, new, options)
ln(list, destdir, options)
ln_s(old, new, options)
ln_s(list, destdir, options)
ln_sf(src, dest, options)
cp(src, dest, options)
cp(list, dir, options)
cp_r(src, dest, options)
cp_r(list, dir, options)
mv(src, dest, options)
mv(list, dir, options)
rm(list, options)
rm_r(list, options)
rm_rf(list, options)
install(src, dest, mode = &lt;src's&gt;, options)
chmod(mode, list, options)
chmod_R(mode, list, options)
chown(user, group, list, options)
chown_R(user, group, list, options)
touch(list, options)
copy_entry(src, dest, preserve = false, dereference = false)
copy_file(src, dest, preserve = false, dereference = true)
copy_stream(srcstream, deststream)
remove_entry(path, force = false)
remove_entry_secure(path, force = false)
remove_file(path, force = false)
compare_file(path_a, path_b)
compare_stream(stream_a, stream_b)
uptodate?(file, cmp_list)
</pre>
<br />
<br />FileUtils::Verbose -- :verbose =&gt; true
<br />FileUtils::NoWrite -- :noop =&gt; true
<br />FileUtils::DryRun&nbsp; -- :verbose =&gt; true, :noop =&gt; true
<br />
<br />trick:
<br /><pre name="code" class="ruby">class &lt;&lt; FileUtils
...
end</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">Weak Reference</span></strong></span>
<br />
<br /><pre name="code" class="ruby">
require 'weakref'
foo = ...
foo = WeakRef.new foo
ObjectSpace.garbage_collect
</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">Scanf</span></strong></span>
<br />
<br />String#scanf, IO#scanf, and Kernel#scanf. Kernel#scanf is a wrapper around STDIN.scanf
<br /><pre name="code" class="ruby">
require 'scanf.rb'
arr = scanf("%d%s")
</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">Hash 持久化</span></strong></span>
<br /><pre name="code" class="ruby">
require 'pstore'
ps = PStore.new "your_store.file"
ps.transaction do
ps[:k] = v
end
ps.transaction true do
puts ps.roots
puts ps[:k]
end
</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">Make Makefile</span></strong></span>
<br /><pre name="code" class="ruby">
require 'mkmf'
create_makefile(target, srcprefix = nil)
</pre>
<br />
<br /><pre name="code" class="ruby">append_library(libs, lib)
arg_config(config, *defaults, &amp;block)
cc_command(opt="")
check_sizeof(type, headers = nil, &amp;b)
checking_for(m, fmt = nil) {|| ...}
checking_message(target, place = nil, opt = nil)
config_string(key, config = CONFIG) {|s| ...}
configuration(srcdir)
cpp_command(outfile, opt="")
cpp_include(header)
create_header(header = "extconf.h")
create_tmpsrc(src) {|src| ...}
dir_config(target, idefault=nil, ldefault=nil)
dir_re(dir)
dummy_makefile(srcdir)
egrep_cpp(pat, src, opt = "", &amp;b)
enable_config(config, *defaults) {|config, *defaults| ...}
find_executable(bin, path = nil)
find_executable0(bin, path = nil)
find_header(header, *paths)
find_library(lib, func, *paths, &amp;b)
have_func(func, headers = nil, &amp;b)
have_header(header, &amp;b)
have_library(lib, func = nil, headers = nil, &amp;b)
have_macro(macro, headers = nil, opt = "", &amp;b)
have_struct_member(type, member, headers = nil, &amp;b)
have_type(type, headers = nil, opt = "", &amp;b)
have_var(var, headers = nil, &amp;b)
init_mkmf(config = CONFIG)
install_dirs(target_prefix = nil)
install_files(mfile, ifiles, map = nil, srcprefix = nil)
install_rb(mfile, dest, srcdir = nil)
libpathflag(libpath=$DEFLIBPATH|$LIBPATH)
link_command(ldflags, opt="", libpath=$DEFLIBPATH|$LIBPATH)
log_src(src)
macro_defined?(macro, src, opt = "", &amp;b)
map_dir(dir, map = nil)
merge_libs(*libs)
message(*s)
mkmf_failed(path)
modified?(target, times)
pkg_config(pkg)
rm_f(*files)
scalar_ptr_type?(type, member = nil, headers = nil, &amp;b)
scalar_type?(type, member = nil, headers = nil, &amp;b)
try_compile(src, opt="", &amp;b)
try_constant(const, headers = nil, opt = "", &amp;b)
try_cpp(src, opt="", &amp;b)
try_do(src, command, &amp;b)
try_func(func, libs, headers = nil, &amp;b)
try_link(src, opt="", &amp;b)
try_link0(src, opt="", &amp;b)
try_run(src, opt = "", &amp;b)
try_static_assert(expr, headers = nil, opt = "", &amp;b)
try_var(var, headers = nil, &amp;b)
what_type?(type, member = nil, headers = nil, &amp;b)
winsep(s)
with_cflags(flags) {|| ...}
with_config(config, *defaults) {|config, *defaults| ...}
with_cppflags(flags) {|| ...}
with_destdir(dir)
with_ldflags(flags) {|| ...}
xpopen(command, *mode, &amp;block)
xsystem(command)
</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">Option Parse</span></strong></span>
<br />这幅图很 pp
<br /><pre name="code" class="ascii art">
+--------------+
| OptionParser |&lt;&gt;-----+
+--------------+ | +--------+
| ,-| Switch |
on_head --------&gt;+---------------+ / +--------+
accept/reject --&gt;| List |&lt;|&gt;-
| |&lt;|&gt;- +----------+
on -------------&gt;+---------------+ `-| argument |
: : | class |
+---------------+ |==========|
on_tail --------&gt;| | |pattern |
+---------------+ |----------|
OptionParser.accept -&gt;| DefaultList | |converter |
reject |(shared between| +----------+
| all instances)|
+---------------+</pre>
<br /><strong>Minimal example</strong>
<br /><pre name="code" class="ruby">
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
options[:verbose] = v
end
end.parse!
p options
p ARGV
</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">IO</span></strong></span>
<br /><pre name="code" class="IO Mode">
Mode | Meaning
-----+--------------------------------------------------------
"r" | Read-only, starts at beginning of file (default mode).
-----+--------------------------------------------------------
"r+" | Read-write, starts at beginning of file.
-----+--------------------------------------------------------
"w" | Write-only, truncates existing file
| to zero length or creates a new file for writing.
-----+--------------------------------------------------------
"w+" | Read-write, truncates existing file to zero length
| or creates a new file for reading and writing.
-----+--------------------------------------------------------
"a" | Write-only, starts at end of file if file exists,
| otherwise creates a new file for writing.
-----+--------------------------------------------------------
"a+" | Read-write, starts at end of file if file exists,
| otherwise creates a new file for reading and
| writing.
-----+--------------------------------------------------------
"b" | (DOS/Windows only) Binary file mode (may appear with
| any of the key letters listed above).
</pre>
<br />
<br />IO 内容繁多,光列个表木有效果 ……
<br /><a target="_blank" href="http://www.ruby-doc.org/core/classes/IO.html">http://www.ruby-doc.org/core/classes/IO.html</a>
<br />
<br />注:
<br />
<br /><span style="color: blue;">*参数中多次出现的 fd 为 file descriptor</span>
<br />stdin: 0, stdout: 1, stderr: 2
<br />
<br /><span style="color: blue;">*IO 本身提供了一些迭代方法,不需要 read 再迭代:</span>
<br />each_byte, each_line(sep_string)
<br />
<br /><span style="color: blue;">*一些罕见的函数:</span>
<br />close_write 关写不关读, lineno 行号, pid 进程号
<br />
<br /><span style="color: blue;">*getc 0..255</span>
<br />
<br /><span style="color: blue;">*tty?</span>
<br />File.new("/dev/tty").isatty #=&gt; true
<br />
<br /><span style="color: blue;">*非阻塞</span>
<br />ios.read_nonblock(maxlen) #=&gt; string
<br />ios.read_nonblock(maxlen, outbuf) #=&gt; outbuf
<br />ios.write_nonblock(string) #=&gt; integer
<br />下面两项没得可读的时候才阻塞:
<br />ios.readpartial(maxlen) #=&gt; string
<br />ios.readpartial(maxlen, outbuf) #=&gt; outbuf
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">Scanf</span></strong></span>
<br /><pre name="code" class="ruby">a = "%d %s %.2f" % [12,'ok',5.2]
require 'scanf'
a.scanf "%d %s %f"
</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">StringIO</span></strong></span>
<br /><pre name="code" class="ruby">require 'stringio'
StringIO.new 'ahahahaha'</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">Glob</span></strong></span>
<br />用文件匹配符列举所有文件
<br /><pre name="code" class="ruby">
Dir.glob '**/*.png'
Dir.glob '**/*/'</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">StrScan</span></strong></span>
<br /><pre name="code" class="ruby">require 'strscan'
s = StringScanner.new
s.eos?
s.scan /\d+/
</pre>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">Find</span></strong></span>
<br /><pre name="code" class="ruby">require 'find'
Find.find 'xx pic' do |path|
if File.basename(path)[0] == ?. # 可以跳过整个目录,提升一下效率
next
else
...
end
end</pre>
如何消灭继承……
关于扩展和继承,想到这么一些东西…… 举个例子:
<br />
<br /><pre name="code" class="ruby">
module Monadic
### def some methods ... ###
end
</pre>
<br />
<br />扩展法:
<br /><pre name="code" class="ruby">
def Monad x, *xs
Proc.new do |f|
f[x,*xs]
end.extend Monadic
end
</pre>
<br />
<br />继承法:
<br /><pre name="code" class="ruby">
class Monad &lt; Proc
def initialize x, *xs
super{|f| f[x, *xs]}
end
include Monadic
end
</pre>
<br />
<br />Monad(12) 比 Monad.new(12) 少一个词,从剃刀原则考虑,不该用继承 ……
<br />(每次敲 initialize 这个词都让我很痛苦,总要想 l 是否要双写,词尾是否要带 e ……)
<br />小小要注意的是:大写字母的函数如果不接受参数,就得给个括号,否则当做常量寻找。
<br />
<br />另外大写字母函数和同名常量可以共存 …… 这上面又能搞很多鬼了 <span style="font-size: x-large;">| ・´ェ`・|</span>
<br />
<br />Mark 一点关于 closure 和漏内存的问题:
<br /><a target="_blank" href="http://olabini.com/blog/2007/12/ruby-closures-and-memory-usage/">http://olabini.com/blog/2007/12/ruby-closures-and-memory-usage/</a>
<br />
<br />Hacker 部分:
<br /><pre name="code" class="ruby">
12&gt; puts class A;3;end
#=&gt; 3
13&gt; class &lt;&lt; Object.new; proc{|x|x*2}; end
#=&gt; #&lt;Proc:0x02d6b828@(irb):13&gt;
</pre>
[Cici] 20 行写个支持高亮和折叠的编辑器 ……
像不像 <a target="_blank" href="http://www.iteye.com/topic/360243">Sentient DSL</a> ?
<br />
<br />行号显示语法高亮自动缩进代码折叠都有鸟…… 连我都觉得自己太帅了……
<br />
<br /><img src="/upload/attachment/117182/68548236-fdab-3043-bf6a-9bf9d8f5aa61.png" />
<br />
<br />现在 lexer 是 ruby only,其它语言得搬运一遍 Scite 的 properties …… 还是体力活。
<br />
<br />下面计划是实现 snippet ~~
<br />
<br />另外 Scintilla 这个东西没有 color scheme 系统,每种语言的语法元素不对应,得一项一项的设 …… 想办法写个 facade ?
Enumerator ---- ListMonad
事情的起因:<a target="_blank" href="http://www.iteye.com/post/1059480?page=1">http://www.iteye.com/post/1059480?page=1</a>
<br />
<br />继续跑题,再看看 List Monad 的对应物
<br /><pre name="code" class="haskell">
*Main&gt; :m + Monad
*Main Monad&gt; let to_s x = [show x]
*Main Monad&gt; [1..10] &gt;&gt;= to_s
["1","2","3","4","5","6","7","8","9","10"]
</pre>
<br />
<br />这部分就是 1.9 only 了 ……
<br /><pre name="code" class="ruby">
10&gt; (1..10).map
#=&gt; #&lt;Enumerator:0xf6dac4&gt;
11&gt; _.each &amp;:to_s
#=&gt; ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
</pre>
<br />
<br />或者倒过来:
<br /><span style="color: red;">(1..10).enum_for(:to_s).each{}</span>
<br />
<br />下面就开始魔幻了 ……
<br /><pre name="code" class="ruby">
12&gt; class Enumerator
def &gt;&gt; sym; self.each &amp;sym; end
end
#=&gt; nil
15&gt; (1..10).map &gt;&gt; :to_s
#=&gt; ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]</pre>
<br />
<br /><pre name="code" class="ruby">
16&gt; module Enumerable;def &gt;&gt; x;self.map &amp;x; end; end
19&gt; (1..10) &gt;&gt; :to_s
#=&gt; ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
20&gt; (1..10) &gt;&gt; method(:print)
12345678910#=&gt; [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil]
</pre>
<br />
<br />这说明了 map 可以当 List Monad 用。 这“优雅”得阳春白雪的 ……
<br /><pre name="code" class="ruby">
s = 0
(1..10) &gt;&gt;-&gt; x{x*x} &gt;&gt;-&gt; x{s+=x} &gt;&gt; method(:puts)
</pre>
<br />
<br />下面有点无聊 …… :
<br /><pre name="code" class="ruby">class Symbol;def -@;method(self);end;end
(1..10) &gt;&gt;-&gt; x{x*x} &gt;&gt; :to_s &gt;&gt;-&gt; x{s+=x} &gt;&gt;- :puts</pre>
<br />
<br />开始进入颜文字阶段:
<br /><pre name="code" class="ruby">
module Enumerable
def &gt;&gt; x
self.inject first, &amp;x
end
end
class Symbol
def + @
-&gt;a,x{ a.send self, x }
end
end
(1..10) &gt; ( +:+ )
#=&gt; 55
</pre>
<br />
<br />
<br />29&gt; 10.times
<br />#=&gt; #&lt;Enumerator:0xac2a10&gt;
<br /><span style="color: red;">30&gt; _ &gt;&gt; (+:+)</span>
<br />#=&gt; 45
<br />
<br />
<br />再推广一下,左边是数据集,右边是指令流,用 &gt;&gt; 连接起来,又一种新语言诞生了 ……
闲聊收集
<p><strong><span style="color: #3366ff;">当时喝水被呛到了</span>
</strong>
<br>
jiyanliang  @night_stalker 我囧,超人是我!<br>
2009-03-28 通过网页 回复 night_stalker <br><br><strong><span style="color: #3366ff;">FX mm 的第一个头像:<span style="color: #3366ff;">蓝发</span>
<span style="color: #ff6600;">妖</span>
<span style="color: #008000;">瞳</span>
90度</span>
</strong>
<br>
RednaxelaFX  @night_stalker 是少女对魔人,简称VVV的作品里的一个主要女性角色,名橋 ルチア<br>
2009-03-24 通过网页 <br><span style="color: #800080;">注:第二个头像:斯美拉基诺列嘉,没找到闲聊记录 ……</span>
<br><span style="color: #999999;">第三个头像:天然的什么来着 ……?存在感 ……</span>
<br><br><strong><span style="color: #3366ff;">一些口实</span>
</strong>
<br>
量产型人型自走炮  @night_stalker 我是由衣控 恩 PS:我已经到了面不改色在学校看电击hime的年纪了XSK...<br>
2009-04-16 通过网页 回复 night_stalker <br><br><strong><span style="color: #3366ff;">我、我才不是学生呢!</span>
</strong>
<br>
zy8643954  @night_stalker 你是学生吗?感觉你的空闲时间太多了。呵呵<br>
2009-05-21 通过网页 <br><br><strong><span style="color: #3366ff;">oz6 是个傲娇</span>
</strong>
<br>
ozzzzzz  @night_stalker 本就不喜欢看的人太多 多三成我就不会写了 别说多一倍<br>
2009-05-08 通过网页 <br><br><strong><span style="color: #3366ff;">读蔡学镛博客</span>
</strong>
<br>
night_stalker  zz:「這個孩子的生肖和出生時辰缺『龍』,所以名字中要多寫幾個『龍』;天格和地格的筆畫加總必須為質數,長大以後才能成為傑出的科學家。」<br>
2009-03-21 通过Firefox插件<br><br><strong><span style="color: #3366ff;">同上,软件起名要小心</span>
</strong>
<br>
night_stalker SH Image Toolkit 的缩写……<br>
2009-03-21 通过Firefox插件 <br><br><strong><span style="color: #3366ff;">从此开始不刷题</span>
</strong>
<br>
night_stalker  @RednaxelaFX 答题和山口山做任务刷声望差不多……<br>
2009-03-18 通过Firefox插件 回复 RednaxelaFX <br><br><strong><span style="color: #3366ff;">不流行音乐</span>
</strong>
<br>
night_stalker  微软 mix 09 走红歌曲: the Nom Nom Nom Nom Nom song !!! http://music.atl.me/nom/ !!!<br>
2009-03-25 通过Firefox插件 <br><br><strong><span style="color: #3366ff;">恍然大悟</span>
</strong>
<br>
night_stalker  原来 _|_ 就是不合法的 _ ...<br>
2009-04-17  通过Firefox插件  <br><br><strong><span style="color: #3366ff;">我是 swing 黑</span>
</strong>
<br>
night_stalker 又给我这个 swing 黑找到新论据了~ http://docs.google.com/Present?docid=dg93d8cv_68c2xw9jd5&amp;skipauth=true<br>
2009-05-29 通过Firefox插件 </p>
<p> </p>
<p><strong><span style="color: #3366ff;">换头记</span>
</strong>
<br>
night_stalker  换头了,新鲜 ps dunk mask 一只<br>
2009-04-25 通过Firefox插件 </p>
杂记. debug.exe
每台 windows 上都有的活化石呀~~~~~~~
<br />
<br /><pre name="code" class="console">
D:\asm&gt;debug sam.txt
-d
143C:0100 53 61 6D 0D 0A 77 61 73-0D 0A 61 0D 0A 6D 61 6E Sam..was..a..man
143C:0110 2E 0D 0A 0D 0A 00 00 00-00 00 00 00 34 00 2B 14 ............4.+.
143C:0120 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
143C:0130 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
143C:0140 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
143C:0150 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
143C:0160 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
143C:0170 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
</pre>
<br />
<br />命令简介:
<br />
<br />d:dump
<br /><pre name="code" class="debug">
d 143c:0112
d 0155</pre>
<br />
<br />e:edit (空格:下一字节)
<br /><pre name="code" class="debug">e 010c
0A.6a</pre>
<br />
<br />q:quit
<br />
<br />w:write
<br />
<br />r:registers
<br /><pre name="code" class="debug">
AX=0000 BX=0000 CX=0005 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=143C ES=143C SS=143C CS=143C IP=0100 NV UP EI PL NZ NA PO NC
143C:0100 7768 JA 016A</pre>
<br />
<br />IP = instruct pointer
<br />CX 寄存器保存着文件的大小,当编辑完内存,想要保存的时候,修改 CX:
<br /><pre name="code" class="debug">
r cx
13
w</pre>
<br />
<br />一些无聊的词语……
<br />real mode flat model
<br />real mode segmented model
<br />protected mode flat model
<br />gonzo memory model
<br />
<br />
State Lexer
写了个简单的带状态词法分析器。
<br />
<br />所谓带状态的,就是每个状态相当于一个简单的词法分析器 ……
<br />state_lexer.rb: <a target="_blank" href="http://gist.github.com/138251">http://gist.github.com/138251</a>
<br />
<br />使用例子:一个从左到右的四则运算计算器。
<br /><pre name="code" class="ruby">require 'state_lexer'
require 'pp'
# globals
num = nil
operator = nil
# setup
l = StateLexer.new :states =&gt; [:left, :operator, :right]
# default rule for all states
l.rule :blank =&gt; /\s+/
l.state = :left
l.rule :float =&gt; /\d+\.\d+/, :int =&gt; /\d+/ do |t|
l.state = :operator
num = t.to_f
end
l.state = :operator
l.rule :operator =&gt; /[\+\-\*\/]/ do |t|
l.state = :right
operator = t
end
l.state = :right
l.rule :float =&gt; /\d+\.\d+/, :int =&gt; /\d+/ do |t| # 这有个 hash 顺序的问题 ……
l.state = :operator
num = num.send operator, t.to_f
end
# initialize
l.state = :left
str = '12 + 5 - 3'
l.scan str
# run
while l.next_token; end
puts "#{str} = #{num}"
</pre>
<br />
<br />另外小改一下还能做 racc 的词法分析器,虽然比 rex 慢一点,不过比 rex 动态一些,也不用生成一遍 ……
OCRA
<a target="_blank" href="http://rubyforge.org/projects/ocra">http://rubyforge.org/projects/ocra</a>
<br />
<br /><a target="_blank" href="http://github.com/larsch/ocra">http://github.com/larsch/ocra</a>
<br />
<br />(水果党和 linuser 先站一边去 ……)
<br />One-Click Ruby Application,就是把解释器、gem 什么的打包在一起做成独立 exe。
<br />&nbsp; 比 rubyscript2exe 和 exerb 先进,支持 1.9。
<br />
<br />安装:
<br /><pre name="code" class="console">gem install ocra</pre>
<br />或者下载 stand alone <span style="color: gray;">not</span> complex 的 .exe
<br />
<br />假设要把 testo.rb 做成 exe,只需:
<br /><pre name="code" class="console">
ocra.rb.bat testo.rb
</pre>
<br />
<br />输出看起来像这样(它把用到的东西都打包到 exe 里面了):
<br />=== Loading script to check dependencies
<br />testo vooo
<br />=== Building testo.exe
<br />m src
<br />a src\testo.rb
<br />m bin
<br />a bin\ruby.exe
<br />a bin\msvcr100-ruby191.dll
<br />a bin\MSVCR100.dll
<br />m lib
<br />m lib\ruby
<br />m lib\ruby\1.9.1
<br />m lib\ruby\1.9.1\i386-mswin32_100
<br />m lib\ruby\1.9.1\i386-mswin32_100\enc
<br />a lib\ruby\1.9.1\i386-mswin32_100\enc\encdb.so
<br />a lib\ruby\1.9.1\i386-mswin32_100\enc\euc_kr.so
<br />a lib\ruby\1.9.1\i386-mswin32_100\enc\gb2312.so
<br />m lib\ruby\1.9.1\i386-mswin32_100\enc\trans
<br />a lib\ruby\1.9.1\i386-mswin32_100\enc\trans\transdb.so
<br />a lib\ruby\1.9.1\i386-mswin32_100\enc\gbk.so
<br />a lib\ruby\1.9.1\rubygems.rb
<br />e RUBYOPT rubygems
<br />e RUBYLIB
<br />l bin\ruby.exe ruby.exe&nbsp; \src\testo.rb
<br />=== Compressing
<br />=== Finished (Final size was 781622) - 只有 781k 的 standalone
<br />
<br />一些琐碎的东西:
<br />
<br />&nbsp;&nbsp;&nbsp; <li> ocra 之前,路径变量中应该包含 ruby_home\bin,ocra 是根据 path 中找到的第一个 ruby 解释器来决定库文件位置的。设定路径变量例:
</li><pre name="code" class="console">set path=d:\Ruby\ruby1.9.1\bin;%path%</pre>
<br />
<br />&nbsp;&nbsp;&nbsp; <li> 需要 win32-api gem,如果你的 ruby 不是官方 1.8.x 二进制,安装 win32-api gem 前记得先把编译器环境设好。
</li>
<br />&nbsp;&nbsp;&nbsp; <li> 一般 ocra 一个文件就行了(例如你要打包一个 rails app 的话,就去 ocra.rb.bat script\server)
</li>有些依赖关系不能通过 require 或者 load 体现,得手动添加。例子:(添加图片和一个目录)
<br /><pre name="code" class="cosole">ocra.rb.bat mainscript.rb someimage.jpeg docs/</pre>
<br />
<br />&nbsp;&nbsp;&nbsp; <li> 对于 GUI 程序,在 main loop 之前加个判断,避免在打包过程中启动程序弹出窗口:
</li><pre name="code" class="ruby">
unless defined? Ocra
app.main_loop
end</pre>
<br />
<br />&nbsp;&nbsp;&nbsp; <li> 注意工作目录,最简易的手段是加上
</li><pre name="code" class="ruby">Dir.chdir File.dirname __FILE__</pre>
<br />
<br />&nbsp;&nbsp;&nbsp; <li> 某些情况可能需要 mingw 编译 stub,所以到 <a target="_blank" href="http://rubyinstaller.org/downloads/">http://rubyinstaller.org/downloads/</a> 下载一个 devkit 可以有备无患。
</li>
<br />
<br />可用选项:
<br /><pre name="code" class="options">
--dll dllname 将额外的 dll 包含进 bin 目录
--no-lzma 取消可执行文件的 LZMA 压缩(体积大一点,运行是否快一点就看你硬盘不是/是 SSD 了)
--quiet 格林..达姆自己
--help 显示帮助
--windows 产生窗口程序(rubyw.exe)
--console 产生控制台程序(ruby.exe)
--no-autoload 不预先加载/包含脚本文件的 autoloads(感觉对速度没什么影响)
--icon &lt;ico&gt; 自定图标
--version 显示版本号</pre>
Hacking JavaEye 问答频道
理解能力要好:
<br />一些提问者的问题表述不清晰,所以要猜 …… 如果你是自个考上大学的,那么加些常识猜到他们问什么不难 ……
<br />
<br />要掌握一点基本功:
<br />利用 google 和 wiki 寻找问题的答案。
<br />一些人用 baidu 搜问题,所以没搜到答案。一些人搜索的方式不对,所以没搜到答案。
<br />《hacking google》可能有些帮助 …… (omg 我竟然看过这本书)
<br />如果你上学时,学校的图书馆开过文献检索的相关课程而你有听过,就更不成问题。
<br />
<br />速度很重要:
<br />如果提问者比较焦灼,在线等待,手最快的就捞到了。一些基本的时效性工作是:订阅问答频道、组队、订阅对手。
<br />
<br />不能算技巧的技巧:
<br /><div class="quote_title">引用</div><div class="quote_div">朋友,问题要自动关闭啦,结分哦</div>
<br />这句话的玄机在哪里呢?&nbsp;
<br />如果提问者看到提醒,选了最佳回答 —— 没损失。
<br />如果提问者还是没看到提醒,问题自动关闭了会给所有回答者平均分 ……
<br />问答频道的问题非常多,全部问题都这么刷一遍,分数就滚滚的来了。
<br />
<br /> -- 还有些黑魔法慢慢写 ……
bluecloth 安装小折腾
看到这个问题: <a target="_blank" href="http://www.iteye.com/problems/20031">BlueCloth 安装不上</a> ……
<br />
<br />跑到 <a target="_blank" href="http://www.deveiate.org/projects/BlueCloth/">官网</a> 上一看,BlueCloth 是 1.0 的,bluecloth 是 2.0 的 …… 这大小写的区别 …… 囧啊。
<br />
<br /><pre name="code" class="console">gem install bluecloth</pre>
<br />
<br />报错,需要本地编译 C 扩展。
<br />
<br />打开 VC2008 控制台。把 <a target="_blank" href="http://night-stalker.iteye.com/admin/blogs/407143">dll hell 终极解决方案</a> 中提到的 lib\ruby\1.8\i386-mswin32\config.h 的 VC 版本限制注释掉。(如果你有 VC6,直接打开控制台即可)
<br />
<br /><pre name="code" class="console">gem install bluecloth</pre>
<br />
<br />在编译本地扩展的时候出错 …… 提示 unresolved external _bzero
<br />google 了一下,原来 bzero 是 memset 的 0 版,不是标准 C 的函数,改用 memset 好了。
<br />
<br />转到 %ruby_home%\lib\ruby\gems\1.8\gems\bluecloth-2.0.4\ext 目录里,
<br />搜到 bzero 这个函数(在 generate.c 里面),
<br />把 <span style="color: blue;">bzero</span>(&amp;key, sizeof key) 改成 <span style="color: red;">memset</span>(&amp;key, <span style="color: red;">0,</span> sizeof key)
<br />
<br />这回编译应该就能通过了,尝试一下:
<br /><pre name="code" class="console">
nmake</pre>
<br />
<br />注意这个时候 gem 还没安装好,也就是 ruby 的 loadpath 不会认它。
<br />转到 %ruby_home%\lib\ruby\gems\1.8\gems\bluecloth-2.0.4\ 里面 rake -T 一下,看到有个 gem 任务,于是 rake gem,在 pkg 目录里面产生了一个 .gem 文件,然后本地安装这个 gem:
<br /><pre name="code" class="console">
cd pkg
gem install bluecloth-2.0.4.gem</pre>
<br />
<br />gem 安装完毕,但是事情还没完 …… 因为我没有 VC6,用的 VC2008 编译器 …… 但是 ruby 是 1.8.6 官方 binary,捆绑的运行时是 VC6 版本的(如果你用 VC6,下面就没你的事了,bye bye)
<br />
<br />再次参照之前我写的 <a target="_blank" href="http://night-stalker.iteye.com/admin/blogs/407143">dll hell 终极解决方案</a> …… 转到 ext 目录下,复制这个 manifest 内容到 bluecloth_ext.so.manifest 中。(这个是 VC2008 only —— VC90.CRT。如果是其它版本,请自行上网搜索对应的 VC 版本和 publicKeyToken …… 我对 mt 工具的使用不熟,貌似对应版本的 mt 可以自动做一个这种 manifest 出来 …… 望有心人告知)
<br /><pre name="code" class="xml">&lt;?xml version='1.0' encoding='UTF-8' standalone='yes'?&gt;
&lt;assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'&gt;
&lt;trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"&gt;
&lt;security&gt;
&lt;requestedPrivileges&gt;
&lt;requestedExecutionLevel level='asInvoker' uiAccess='false' /&gt;
&lt;/requestedPrivileges&gt;
&lt;/security&gt;
&lt;/trustInfo&gt;
&lt;dependency&gt;
&lt;dependentAssembly&gt;
&lt;assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='9.0.21022.8' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' /&gt;
&lt;/dependentAssembly&gt;
&lt;/dependency&gt;
&lt;/assembly&gt;
</pre>
<br />
<br />将 manifest 绑到 so 文件中:
<br /><pre name="code" class="console">mt -manifest bluecloth_ext.so.manifest -outputresource:bluecloth_ext.so;2
nmake
nmake install</pre>
<br />
<br />大功告成……
<br /><pre name="code" class="irb">01&gt; require 'bluecloth'
#=&gt; true</pre>
<br />
<br />感慨: 我就是爱折腾 ……
扯淡一下 lambda
lambda 演算中,可以用 lambda 定义所有的东西。
<br />次元门: <a target="_blank" href="http://zh.wikipedia.org/w/index.php?title=%CE%9B%E6%BC%94%E7%AE%97&amp;variant=zh-cn">wiki 的 lambda 定义</a> (wiki 里面是用 . 来分隔参数和返回值的)
<br />
<br /><strong><span style="color: #1133ff;">下面为了表述方便,约定一些记号:</span></strong>
<br /><pre name="code" class="">1.下面的 lambda 用了 haskell 里面的一些象形文字: \ 表示 λ, -&gt; 表示 → ,参数用空格分开。
例如 \x y -&gt; x + y 就相当于 javascript 的 function(x, y){return x + y}
2.= 号代表定义,=&gt; 代表归约或者计算。</pre>
<br />
<br />
<br />只靠基本定义和 3 条演算规则,就足够构成 lambda 世界的所有东西了。<strong><span style="color: #1133ff;">所以 lambda 是最简单的编程语言之一。</span></strong>
<br />
<br />lambda 可以返回 lambda,于是多参数的 lambda 可以用单参数的 lambda 定义:
<br /><pre name="code" class="">譬如 \x y -&gt; x 其实是 \x -&gt; \y -&gt; x</pre>
<br />
<br />非负整数的 lambda 定义(church 数):
<br /><pre name="code" class="haskell">
0 = \x -&gt; x
1 = \x -&gt; \f -&gt; f x
2 = \x -&gt; \f -&gt; f f x
3 = \x -&gt; \f -&gt; f f f x
...</pre>
<br />此基础上可以定义四则运算、浮点数等东西(知道可以就行了…… 略去)
<br />字符串就是字符的集合,而字符本质上就是整数(也略去……)
<br />
<br />布尔值的 lambda 定义:
<br /><pre name="code" class="haskell">true = \x y -&gt; x
false = \x y -&gt; y</pre>
<br />
<br />
<br /><strong><span style="color: #1133ff;">基本数据类型齐全,下面定义控制结构。</span></strong>
<br />
<br />控制结构有两个关键:一个是分支判断,另一个是循环。
<br />
<br />分支判断的 lambda 定义:
<br />下面 p, t, f 分别是断言、真子句和假子句,断言 p 可以是 true 或者 false
<br /><pre name="code" class="haskell">cond = \p t f -&gt; p t f</pre>
<br />如果断言为真,则返回 t,如果断言为假,则返回 f
<br />
<br />应用例子:
<br /><pre name="code" class="haskell">cond true 1 2
-// =&gt; (\p t f -&gt; p t f) (\x y -&gt; x) 1 2 =&gt; (\x y -&gt; x) 1 2 =&gt; 1
cond false 1 2
-// =&gt; (\p t f -&gt; p t f) (\x y -&gt; y) 1 2 =&gt; (\x y -&gt; y) 1 2 =&gt; 2</pre>
<br />
<br />循环可以用递归定义。lambda 没有递归定义(即 = 右边不能出现左边的符号,归约过程中也不能出现最左边的函数符号),但是递归结构可以用组合子和非递归函数的组合定义 —— 如 Y 组合子。
<br />
<br />使用 Y-Combinator 定义阶乘函数 fac 就像这样(运用上面定义的 cond):
<br /><pre name="code" class="haskell">g = \f n -&gt; cond (n == 0) 1 (n * (f (n - 1)))
fac = y g</pre>
<br />
<br />3 的阶乘,计算过程就像这样:
<br /><pre name="code" class="">fac 3
=&gt; 3 * (g _ 2)
=&gt; 6 * (g _ 1)
=&gt; 6 * (g _ 0)
=&gt; 6</pre>
<br />
<br />那么如何定义 Y 呢? haskell 支持递归定义和 lazy 求值,所以可以这样定义(不支持 lazy 求值的就会直接无限循环了):
<br /><pre name="code" class="haskell">y = \f -&gt; f (y f)</pre>
<br />
<br />但现在我们还没有递归 …… 为了消除右边的 y,可以改成:
<br /><pre name="code" class="haskell">y = \f -&gt; g g
where
g = \h -&gt; f (h h)</pre>
<br /><pre name="code" class="">其中 g g
=&gt; f (g g)
=&gt; f (f (g g))
=&gt; f (f (f (g g)))
=&gt; ...</pre>
<br />
<br />经过一番折腾,两大控制结构齐备,其它 for、while、loop、switch …… 都不在话下。所有东西都是 lambda 了。
<br />
<br />
<br />
<br />-------------------------------- <strong><span style="color: #1133ff;">收了好处的分隔线</span></strong> --------------------------------
<br />
<br />好处是什么呢?这告诉我们,复杂的世界是由简单的东西构成的。
<br />什么是定理? 一堆 lambda 等价于另一堆 lambda,这就是定理。
<br />什么是优化? 如果定理左边这堆 lambda 算起来很慢,但右边这堆 lambda 算起来很快,那么就用右边这堆代替左边这堆。
<br />什么是真理? 按照 alpha、beta、eta 归约算下去,最后变成 True 的,就是真理。
<br />什么是概念? 一个 lambda。
<br />什么是对象? 一个 lambda。
<br />什么是模式? 一个 lambda。
<br />……
<br />
<br />Duck Typing (如果一个东西 ... 回帖像火星人,那么它就是火星人) 的 lambda 基础:
<br />eta 变换 —— 对于 f 和 g,给定任何 x,f x == g x 总是成立,那么 f 就是 g。
<br />上升到哲学层面,就是人只能借助观测来了解一个事物 ……
<br />就算一个人"整天心神不宁",但是这个人一辈子没买过申花没当过房地产开发商,我们认为他是好人 ……
<br />退一步讲,现实总有些潜规则和副作用,没有这么 pure functional,所以严谨的讲,他基本是好人 ……
<br />
<br />
Potion 的小配方
Potion 是 why 正在搞的顶级机密语言,完成度不高,但是在里面可以看到很多有意思的东西。
<br /><a target="_blank" href="http://hackety.org/potion/">http://hackety.org/potion/</a>
<br /><a target="_blank" href="http://github.com/why/potion/tree/master">http://github.com/why/potion/tree/master</a>
<br />
<br /><img src="http://hackety.org/potion/potion-1.png" />
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">简介</span></strong></span>
<br />
<br />特点:所有东西都是对象,但对象不是全部 —— 所有东西都是函数(好像有句话叫:<strong>连函数都是函数</strong>)
<br />实现:只有不到 10k 行的 C。
<br />和 lua 一样,potion 的字符串都是 immutable 的 —— 字符串的实例创建后就不能改变。
<br />基于寄存器的 VM。
<br />分代 GC。
<br />字节码解释器和 JIT。
<br />
<br /><strong><span style="color: #2266ff;">第一个例子</span></strong>
<br /><pre name="code" class="potion">loop: '呱' print.</pre>
<br />
<br />说明:
<br />冒号开始一个 block(像 python,当然 block 也是对象);
<br />print 是发送给字符串 '呱' 的消息;
<br />句号结束这个 block。
<br />
<br />做的事情相当于:
<br /><pre name="code" class="c">while(true){printf("呱");}</pre>
<br />
<br /><strong><span style="color: #2266ff;">数据结构</span></strong>
<br />List
<br /><pre name="code" class="potion">('奶酪', '面包', '蛋黄酱') at (1) print</pre>
<br />Table (或者说 map)
<br /><pre name="code" class="potion">(语言='Potion', pointless=true) at (key='语言') print</pre>
<br />
<br /><strong><span style="color: #2266ff;">函数</span></strong>
<br /><pre name="code" class="potion">减 = (x, y): x - y.
减 (y=10, x=6)</pre>
<br />说明:
<br />这里又见到冒号-句号的组合了~ 其中的 block 定义了函数体;
<br />第二行传给 减 的参数是一个 Table(List 和 Table 和函数参数列表都用了相同的语法:())
<br />
<br />前面说过所有东西都是函数,List 也不例外
<br /><pre name="code" class="potion">吃的 = ('奶酪', '面包', '蛋黄酱')
吃的 (2)</pre>
<br />
<br />String 也是函数,这行代码返回第三个字符
<br /><pre name="code" class="potion">"ヘ(^_^ヘ)(ノ^_^)ノ" (2)</pre>
<br />
<br />function 可以粘到消息(方法)的后面(注:这三位是 why 的漫画里面的角色……)
<br /><pre name="code" class="potion">(狗狗='canine', 猫猫='feline', 狐狐='vulpine') each (小可爱, 名字):
(小可爱, '名叫', 名字) join print.</pre>
<br />
<br />相当于
<br /><pre name="code" class="ruby">{'狗狗'=&gt;'canine', '猫猫'=&gt;'feline', '狐狐'=&gt;'vulpine'}.each{|小可爱, 名字|
print [小可爱, '名叫', 名字].join}</pre>
<br />
<br />block 只是最后一个参数,这样写也可以
<br /><pre name="code" class="potion">(狗狗='canine', 猫猫='feline', 狐狐='vulpine') each ((小可爱, 名):小可爱 print.)</pre>
<br />
<br /><strong><span style="color: #2266ff;">OO 编程</span></strong>
<br />定义一个类:
<br /><pre name="code" class="potion">淫 = class: /名字, /年龄, /性别.
淫 print = ():
('在下法号', /名字, '。') join print.</pre>
<br />注:这个好像 REBOL 呀,REBOL 也是用 / 而不是 . 获得成员引用,好处是调用 REST 方法就和调用普通的方法一样。
<br />
<br />又注:对象的成员变量在内存中是和 C struct 相似的东西,不是像 Ruby 那样的 Hashtable,效率相当高。那么动态的成员如何实现?利用消息就可以了。
<br />
<br />实例化:
<br /><pre name="code" class="potion">甲 = 淫 ()
甲 /名字 string print</pre>
<br />
<br />这个例子,取出了 甲 的 /名字 成员,对其发送 string 消息(相当于 java 的 toString() 或者 ruby 的 to_s),然后打印返回的字符串。由于 /名字 还没设值,显示的结果是 nil。
<br />
<br />子类化(可以理解为继承)
<br /><pre name="code" class="potion">公务员 = 淫 class (职业): /职业 = 职业.
公务员 print = ():
('我叫', /名字, ',是个', /职业, '。') join print.
公务员 ('五毛') print</pre>
<br />class 消息子类化了 淫 并定义了构造函数。
<br />
<br /><strong><span style="color: #2266ff;"> Lick</span></strong>
<br />如果我们写 GUI,可能会这样
<br /><pre name="code" class="potion">app = window(width=200, height=400):
para 'Welcome.'
button 'OK'.</pre>
<br />这里存在的问题是,我们用代码表达数据,而且需要在全局命名空间中定义 window,para 和 button ……
<br />
<br />使用 Lick (如下面的方括号语法)表达树形数据可避免这个问题(译注:这个 idea 是从 E4X 学来的,可是 xml 还是很惹人讨厌,使用类似 lisp 的 s-exp 句式就简洁很多 ……):
<br /><pre name="code" class="potion">app = [window (width=200, height=400)
[para 'Welcome.', button 'OK']]
app first name</pre>
<br />
<br />Lick 的结构很像标签。
<br /><div class="quote_title">hackety 写道</div><div class="quote_div">Every lick can have a name, a table of attributes, and a list of children. </div>
<br />
<br />纯引用 ……
<br /><div class="quote_title">Puzzled People 写道</div><div class="quote_div">Is there a source of how to create objects and mixins or a tutorial/documentation some where out there? —Adkron
<br />
<br />“The mixin interface isn’t there yet.” That’s basically the whole tutorial. — why</div>
<br /><span style="font-size: large;"><strong><span style="color: indigo;">深入一点 -- 关于语法</span></strong></span>
<br />
<br /><ul>
<li> 方法(消息)、块和函数都使用 冒号-句点 语法。
</li><li> Table 和 List 都可用于函数、块的参数列表,也用于 lick 的属性。
</li><li> 除了类名称,以上代码基本都用了小写,但是用小写做类名也可以。
</li></ul>
<br /><strong><span style="color: #2266ff;">编码</span></strong>
<br />源代码总是 UTF-8,所有字符串也是 UTF-8,核心 API 只有 UTF-8 的编码方式。
<br />
<br /><strong><span style="color: #2266ff;">换行</span></strong>
<br />逗号相当于换行,下面两段是等价的:
<br /><pre name="code" class="potion">x = 1
y = 2</pre>
<br /><pre name="code" class="potion">x = 1, y = 2</pre>
<br />反过来也成立:换行相当于逗号:
<br /><pre name="code" class="potion">(语言='Potion'
pointless=true)</pre>
<br />
<br /><strong><span style="color: #2266ff;">空格</span></strong>
<br />东西分得很清楚的时候,空格可以省略,如:
<br /><pre name="code" class="potion">('火腿','火腿子')at(1)print</pre>
<br />
<br /><strong><span style="color: #2266ff;">注释</span></strong>
<br />和 ruby 的行注释一样,# 后面的一行会被编译器忽略
<br /><pre name="code" class="potion"># 全世界的字符串长度都是 10!
String length = (): 10.</pre>
<br />
<br /><strong><span style="color: #2266ff;">语句块</span></strong>
<br /><pre name="code" class="potion">block = :
'呱' print.</pre>
<br />这个块不是立即执行的哦。
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">内建的类型(浮点数还在开发中!)</span></strong></span>
<br />为什么浮点数仍在开发中? 因为 Potion 是跨平台的,也就是 ……
<br /><div class="quote_title">why 写道</div><div class="quote_div">You are right. I am overcompensating. I want Potion to run on the <strong><span style="color: red;">Nintendo DS</span></strong>, the <strong><span style="color: orange;">PSP</span></strong>, the <strong><span style="color: green;">iPhone</span></strong>.</div>
<br />
<br /><strong><span style="color: #2266ff;">真假空</span></strong>
<br />nil true false 大家耳熟能详 ……
<br />nil 的类是 NilKind,而 true、false 的类是 Boolean。
<br />所有非 nil 和 false 的值都被认为是 —— 阳性的 ……(和 ruby 一样)
<br />
<br /><strong><span style="color: #2266ff;">Number</span></strong>
<br />Potion 现在只支持小整数,32 位或者 64 位,因处理器而异,例:
<br /><pre name="code" class="potion">-25
0xFF</pre>
<br />
<br /><strong><span style="color: #2266ff;">String</span></strong>
<br />单引号字符串中只有一个转义字符:'' —— 两个单引号表示一个单引号
<br />双引号字符串包含更多的转移字符:
<br /><ul>
<li> "\n" 换行
</li><li> "\r" 回车
</li><li> "\t" tab
</li><li> "\uXXXX" Unicode 字符
</li></ul>
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">表达式</span></strong></span>
<br /><div class="quote_title">无名氏 写道</div><div class="quote_div">只要一个语言所有语句都是表达式,它就是函数式语言。</div>
<br />
<br /><strong><span style="color: #2266ff;">if then else</span></strong>
<br />例:
<br /><pre name="code" class="potion">作者 =
if (标题 == 'Jonathan Strange &amp; Mr. Norrell'):
'Susanna Clarke'.
elsif (标题 == 'The Star Diaries'):
'Stanislaw Lem'.
elsif (标题 == 'The Slynx'):
'Tatyana Tolstaya'.
else:
'... probably Philip K. Dick'.</pre>
<br />
<br /><strong><span style="color: #2266ff;">loop</span></strong>
<br />无尽循环,例子如第一个。
<br />
<br /><strong><span style="color: #2266ff;">while</span></strong>
<br /><pre name="code" class="potion">i = 8
while (i &gt; 0):
'呱' print
i--.</pre>
<br />
<br /><strong><span style="color: #2266ff;">to 和 times</span></strong>
<br />大家多多少少该注意到了: Potion 没有 for 循环(某小可爱:最鄙视 for 循环了!)
<br />这两个例子和 ruby 差不多。
<br /><pre name="code" class="potion">1 to 5 (a):
a string print.
5 times: '呱' print.</pre>
<br />
<br /><strong><span style="color: #2266ff;">return</span></strong>
<br />没什么特别含义,就是我们平常(非 haskell)用的 return。
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">名字和对象</span></strong></span>
<br />
<br /><strong><span style="color: #2266ff;">变量名</span></strong>
<br />任何不是内建类型或者操作符的 UTF-8 字符串都可以用做变量名
<br /><pre name="code" class="potion">t = true
$$ = [美刀 100]
HTTP = '超揾笨传输协议'
わが身 = self</pre>
<br />使用变量之前一定要先设值(即使是设成 nil),否则将视为发送给 self 的消息。
<br />
<br /><strong><span style="color: #2266ff;">消息</span></strong>
<br />任何不是内建类型或者操作符的 UTF-8 字符串都可以用做消息名
<br />例子:定义一个 Number 的消息并发送
<br /><pre name="code" class="potion">Number $ = ():
[美刀 (数量 = self)].
100 $</pre>
<br />
<br /><strong><span style="color: #2266ff;">查询</span></strong>
<br />对象是否响应某个消息,可以用问号问一问(不响应就返回一个 nil)
<br /><pre name="code" class="potion">if (3 ? 性别):
"原来数字是有性别的,好神奇" print.</pre>
<br />
<br />如果查询字句后面带参数表,则其行为类似 Maybe Monad,其中一环为 nil 的话,后面都不执行,返回 nil
<br /><pre name="code" class="potion">HomePage get = (url):
session = url query ? at ('session').</pre>
<br />
<br /><strong><span style="color: #2266ff;">最后是 path 和 path 查询</span></strong>
<br /><pre name="code" class="potion">BTree = class: /left, /right.
b = BTree ()
b /left = BTree ()
b /right = BTree ()
BTree = class: /left, /right.
b = BTree ()
if (b ? /left):
'left path found!' print.</pre>
<br />
<br />
<br /><img src="/upload/attachment/125595/0a902971-bfda-38d6-a4e4-8f1b6a17a67e.png" />
<br />
<br /><span style="font-size: large;"><strong><span style="color: indigo;">what's more</span></strong></span>
<br />scoped mixin: 你可以在局部作用域内改变对象的行为,但是又不影响它局部作用域外面的行为。
<br />
<br />题外:Matz 想在 ruby 加这个特性很久了,并称之为 selector namespace;Scala 的隐式转换是局部的;C# 3.0 起有命名空间的扩展方法。
无法定位程序输入点 msvcrt.strnicmp ……
今天被这个问题折腾了好久 …… 迟迟不能提交 test 报告 ……
<br />
<br />无法定位程序输入点 msvcrt.strnicmp …… 但是源程序中没有一个地方用 strnicmp 啊 ……
<br />
<br />无奈在 ruby 源代码中搜,发现 win32.h 中有:
<br /><pre name="code" class="C">#define strncasecmp strnicmp</pre>
<br />
<br />新版 VC 里面,strnicmp 已废止,改用 _strnicmp (有个啼笑皆非的 warning 是 _strnicmp is deprecated, please use _strnicmp ……) —— M$ 你的字符串函数太 tm 多了吧!
<br />
<br />这时就要用黑客手段 …… 在 win32.h 198 行附近改一番
<br /><pre name="code" class="C">#if _MSC_VER == 1200
# define strcasecmp stricmp
# define strncasecmp strnicmp
#else
# define strcasecmp _stricmp
# define strncasecmp _strnicmp
#endif</pre>
<br />
MASM 的零碎笔记
主要来自 <a target="_blank" href="http://www.masm32.com/">MASM32</a> 的帮助 Introduction To Assembler (汇编的扩展名是 asm 而不是 ass 的原因值得深思!)
<br />
<br />两个应该打印一份的参考手册
<br />
<br />简单版
<br /><a target="_blank" href="http://www.jegerlehner.ch/intel/">http://www.jegerlehner.ch/intel/</a>
<br />
<br />详尽版
<br /><a target="_blank" href="http://ref.x86asm.net/">http://ref.x86asm.net/</a>
<br />
<br />--------
<br />命令行
<br />ml /c /coff
<br />link /subsystem:console
<br />link /subsystem:windows
<br />lib
<br />cl /Fa
<br />--------
<br />
<br />设置 include 和 lib
<br /><pre name="code" class="bat">@set include=D:\masm32\include;%include%
@set lib=D:\masm32\lib;%lib%</pre>
<br />
<br />hello world
<br /><pre name="code" class="masm">
.386
.model flat, stdcall
option casemap :none
include windows.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib
.data
szCap db 'hello', 0
szTxt db 'world', 0
.code
start:
invoke MessageBox, NULL, addr szTxt, addr szCap, MB_OK
invoke ExitProcess, NULL
end start
;编译用 /subsystem:windows
</pre>
<br />
<br />invoke XXX 相当于从右到左 push arg,然后 call XXX
<br />* push 字符串的 snippet:<pre name="code" class="masm">
lea eax, szTxt
push eax</pre>
<br />
<br />eax 低位 ax,ax 高位 ah, 低位 al
<br />
<br />交换 eax 的高位和 ax
<br /><pre name="code" class="masm">rol eax, 16</pre>
<br />
<br />赋值 byte to dword
<br />方法1.
<br /><pre name="code" class="masm">movzx eax, cl ;zero extend unsigned int
movsx eax, cl ;sign extend signed int</pre>
<br />方法2.
<br /><pre name="code" class="masm">xor eax, eax
mov al, cl</pre>
<br />方法3.
<br /><pre name="code" class="masm">mov al, cl
cbw ;al byte -&gt; ax word
cwde ;ax word -&gt; eax dword</pre>
<br />
<br />可以自由使用的寄存器 eax,ecx,edx
<br />ebx,esi,edi 在 proc 中不能自由使用,如果要,先保存,用完弹回来
<br /><pre name="code" class="masm">push ebx
push esi
push edi
;...
pop edi
pop esi
pop ebx</pre>
<br />
<br />数据类型
<br />1.immediate
<br /><pre name="code" class="masm">mov al, "a" ;char,如果多于 1 个 char 就会说 constant too long
mov edx, 0 ;num</pre>
<br />2.memory
<br /><pre name="code" class="masm">mov al, [esi]
mov edx, lp_mem_var ; 复制变量地址到 edx</pre>
<br />3.register
<br /><pre name="code" class="masm">mov ecx, edx</pre>
<br />
<br />复制内存
<br />1.
<br /><pre name="code" class="masm">mov eax, srcmem
mov dstmem, eax</pre>
<br />2. 如果没有寄存器了
<br /><pre name="code" class="masm">push srcmem
pop dstmem</pre>
<br />
<br />栈操作
<br /><pre name="code" class="masm">push edx
pop eax</pre>
<br />push 32 位,pop 16 位
<br /><pre name="code" class="masm">push edx
pop ax
pop cx</pre>
<br />
<br />地址
<br />1. dereferencing
<br /><pre name="code" class="masm">mov eax, lpvar
mov eax, [eax] ; 对32位的寄存器,可以用 [] 去获得指向的值
mov newvar, eax</pre>
<br />2. 指针
<br /><pre name="code" class="masm">lea eax, my_var</pre>
<br />
<br />编写 lib
<br /><pre name="code" class="masm">.386
.model flat, stdcall
option casemap :none
YourProc PROTO :DWORD, :DWORD
.code
YourProc proc v1:DWORD, v2:DWORD
;...
ret
YourProc endp</pre>
<br />
<br />编写 inc
<br /><pre name="code" class="masm">YourProc PROTO :DWORD, :DWORD</pre>
<br />
<br />编译 lib
<br /><pre name="code" class="masm">ml /c /coff *.asm
lib *.obj /out:yourlib.lib</pre>
<br />
<br />使用 lib
<br /><pre name="code" class="masm">include yourlib.inc
includelib yourlib.lib</pre>
<br />
<br />字符串操作
<br />复制字符串(0 结尾):
<br /><pre name="code" class="masm">cld ;clear direction flag to read forward
mov esi, src
mov edi, dst
label:
lodsb ;load byte from src into AL and inc ESI
stosb ;write AL to dst and inc EDI
cmp al, 0
jne label ;不等即跳</pre>
<br />在 pentium 等更高的 cpu 下面双倍速的版本(pairing: 两个指令可以同时在两条流水线上进行)
<br /><pre name="code" class="masm">mov esi, src
mov edi, dst
label:
mov al, [esi]
inc esi
mov [edi], al
inc edi
cmp al, 0
jne label</pre>
<br />
<br />匿名标签
<br /><pre name="code" class="masm">@@:
jmp @F ;跳向下一个@@
jmp @B ;跳向前一个@@</pre>
<br />
<br />宏: 往 eax 里面填入 rgb
<br /><pre name="code" class="masm">RGB MACRO red, green, blue
xor eax, eax
mov al, blue
rol eax, 8 ; &lt;&lt; 8,(循环式)
mov al, green
rol eax, 8
mov al, red
ENDM</pre>
<br />
<br />调用 stdcall API 的宏
<br /><pre name="code" class="masm">Scall MACRO name:REQ,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20,p21,p22
FOR arg,&lt;p22,p21,p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1&gt;
IFNB &lt;arg&gt; ;; If not blank
push arg ;; push parameter
ENDIF
ENDM
call name ;; call the procedure
ENDM</pre>
<br />
<br />宏内的局部标签和变量
<br /><pre name="code" class="masm">LOCAL MyLabel
LOCAL MyVar</pre>
<br />
<br />高速循环
<br /><pre name="code" class="masm">mov edx, 1000000
label:
;...
dec edx
jnz label</pre>
<br />
<br />以字节为单位的遍历
<br /><pre name="code" class="masm">mov esi, src ; put source address in esi
mov edi, dst ; put destination address in edi
mov ecx, count ; put count in ecx
add ecx, esi ; add address in esi to ecx
; to have an exit condition to test
label:
mov al, [esi]
inc esi
;...
mov [edi], al
inc edi
cmp ecx, esi ; test for exit condition here
jne label ; if not equal, loop again</pre>
<br />
<br />部分展开、完全展开循环提速
<br />
<br />尽量不要使用低效的 loopx, movs, lods, cmps, scas,
<br />将 jcxz 改成<pre name="code" class="masm">
test ecx, ecx
je SLabel</pre>
<br />
<br />struct
<br />成员,数据大小,?表示不初始化为一个值
<br /><pre name="code" class="masm">RECT STRUCT
left DWORD ?
top DWORD ?
right DWORD ?
bottom DWORD ?
RECT ENDS</pre>
<br />
<br />struct 可以在数据区也可以在指令区。struct 的成员是 memory 操作数。
<br /><pre name="code" class="masm">LOCAL Rct :RECT
mov Rct.left, 1</pre>
<br />
<br />用 struct 的地址作参数
<br /><pre name="code" class="masm">invoke ApiCall, addr Rct</pre>
<br />
<br />用 struct 做参数,如下面的声明
<br /><pre name="code" class="masm">MyProc proc p1:DWORD, p2:RECT</pre>
<br />
<br />可以嵌套
<br /><pre name="code" class="masm">MyS STRUCT
rc RECT &lt;&gt;
pt POINT &lt;&gt;
MyS ENDS</pre>
<br />使用
<br /><pre name="code" class="masm">LOCAL m:MyS
m.rc.left</pre>
<br />
<br /><pre name="code" class="masm">ASSUME eax:PTR RECT
mov eax, lpRct
mov [eax].top, 12
ASSUME eax:nothing</pre>
<br />或者
<br /><pre name="code" class="masm">mov eax, lpRct
mov (RECT PTR [eax]).top, 12</pre>
<br />相当于
<br /><pre name="code" class="masm">mov eax, lpRct
mov [eax+4], DWORD PTR 12</pre>
<br />
土法查看 MASM 汇编指令的二进制代码
非常土 ……
<br />
<br />基本原理:
<br />asm 里写进一堆 nop 接着指令,再一堆 nop。
<br />用 ml 编译后用正则匹配 exe 的那堆 \x90 ……
<br />
<br /><pre name="code" class="ruby"># 'nop' series count. it's ok to change to other number
pre, post = 11, 13
# tmp file name
tmp = '_masm_tmp1234'
begin
# nop --&gt; 0x90
if ARGV[0] =~ /\s*nop/
puts '90'
puts 'warning: instructions after nop is ignored'
exit 0
end
# write src
File.open "#{tmp}.asm",'w' do |f|
f &lt;&lt; ".386\n.model flat, stdcall\n.code\nstart:\n"
f &lt;&lt; "#{"nop\n"*pre}#{ARGV.join "\n"}\n#{"nop\n"*post}"
f &lt;&lt; "end start"
end
# build exe
out = `ml /nologo /c /coff #{tmp}.asm`
out &lt;&lt; "\n#{`link /subsystem:console #{tmp}.obj /o#{tmp}`}\n"
# read exe, find pattern of nop (\x90)
a = File.read("#{tmp}.exe")
if RUBY_VERSION &gt;= '1.9.0'
a.force_encoding 'ASCII-8bit'
end
a =~ /#{"\x90"*pre}((?:[\x00-\x8f]|[\x91-\xff])[\x00-\xff]*)#{"\x90"*post}/n
puts $1.unpack("C*").map{|i|"%02x" % i}.join(' ')
rescue Exception =&gt; e
puts out
puts
puts e
ensure
# clean up
`del #{tmp}.*`
end</pre>
<br />
<br />(FX 你是对的 …… 应该保存 ml 和 link 的输出 ……)
<br />
<br />以上代码存为 op.rb,再补个 op.bat
<br /><pre name="code" class="批处理">@echo off
ruby op.rb %*
</pre>
<br />
<br />使用例子(需要(ruby1.8 或 ruby1.9)和 (msam 或者 vc)):
<br /><pre name="code" class="console">D:\asm\cp&gt;op 'ret'
c3
D:\asm\cp&gt;op 'mov eax, "a"'
b8 61 0 0 0</pre>
<br />
<br />ps:其实 intel 手册里面就有,MASM32 IDE 还配了简化版的手册 ……
<br />pss:可以改个查看 C 或者其它语言的版本 ~
Scala 的杂记1:skah-la
惊讶的发现念斯旮旯 …… 不是 scale a ……
<br />
<br />讲道理卖广告的多了,可惜没有实质意义,实作才是正确的方向 ……
<br />
<br />请称呼我为 <span style="color: #0011ee;"><strong>hello world</strong></span> 达人
<br /><pre name="code" class="java">args.foreach(println)</pre>
<br />运行
<br /><pre name="code" class="console">scala s.scala hello world</pre>
<br />
<br />打开 repl 练手,附感想。(进入是 <span style="color: brown;">scala</span>,退出是<span style="color: indigo;">:q</span>)
<br />
<br /><span style="color: #0011ee;"><strong>数据结构</strong></span>
<br />(缺省是 immutable,某些单线程而且性能 critical 的某些极少数几乎遇不到的情况再考虑 import scala.collection.mutable.Xxx)
<br /><pre name="code" class="java">
// Array 没有 sort,谢谢 jonathan_zz 的指正
List(1)++List(4)
Array(1)++Array(4)
Array(61,62)+"abc" // Array 可以和字符串相加产生字符串
// 但是 List 不行,另外 Array 不能被看做字符串 ...
(1::2::3::Nil).filter (3&gt;) // 这个我喜欢,和 haskell 差不多呦
(1::2::3::Nil).filter (_&lt;2) // 不用 _ 就不行 - -
List(1,2):::List(3,4) // trap:如果不小心少打一个冒号就傻蛋了,用++为妙
var m = Map("k"-&gt;"v")++Map("s"-&gt;"c")
m += ("u"-&gt;"v")
m("s")
var s = Set("doe","re")
s += "me"
s.contains("fa")
</pre>
<br />
<br /><span style="color: #0011ee;"><strong>List</strong></span> (最佳实践:List 函数和递归可以完全取代 while 和 for 并且更强大)
<br /><pre name="code" class="java">l(2)
l.head;l.last
l.init;l.tail
l.count(3&gt;_)
l.drop(2)
l.dropRight(2)
l.filter(_.length==4)
l.forall(_.endsWith("s")) // Ruby: l.all?
l.foreach(print)
l.map(_+"y") // 深深喜欢上了下划线 ……
l.mkString(", ") // 全世界都用 join 你偏要 mkString ……
l.reverse
l.sort(_(0).toLowerCase &lt; _(1).toLowerCase)
l.isEmpty
l.length
l.exists(_=="yellow")</pre>
<br />
<br />*** 枚举器可 toList
<br />
<br /><span style="color: #0011ee;"><strong>Tuple </strong></span>((,) 的类型是 Tuple2,依此类推)
<br /><pre name="code" class="java">val t=(99,"bottles")
t._2 // Haskell: Snd t
</pre>
<br />
<br />让我很伤心的是 class 不能当 value 用。这时得用 object ……
<br />泛型用 [] 而不是 &lt;&gt;,泛型好像可以用值参数? 待验证。
<br />
<br /><span style="color: #0011ee;"><strong>类型</strong></span>,值得长篇大论大书特书的主题 ……
<br /><pre name="code" class="java">
def f(x:Int, y:Int):Int = { ...
def f (x:Int) (y:Int) :Int = { ... // 看来还是上一行好点 。。。
def f (p: ty =&gt; boolean) (c:Int) :Array[ty] = ... // 看来上一行目的是 lambda 参数
def f:Unit = 3 // 返回 Unit 实例 【】
def f = 3 // 返回 Int 实例 3 -- 返回类型可以推
</pre>
<br />
<br />下面这几个用法都是<span style="color: brown;">不行</span>的 ……
<br /><pre name="code" class="java">
def f(x)=x // × 为什么 haskell 能推 scala 不能推 ……
def f(x)=x+1 // ×
def f(x)=1 // ×
def f(x:Any)=1+x // ×
</pre>
<br />
<br />Ruby 中检查一个类型响应 '+' 很简单: x.respond_to ? :+
<br />Scala 用 <span style="color: #0011ee;"><strong>structual typing</strong></span> 可以解决部分问题 …… (感谢 FX 的神奇代码)
<br /><pre name="code" class="java">def f(x:{def +(a:Int): Int}) = x + 1
class A{
def +(i:Int)=i*2
}
f (new A)</pre>
<br />
<br /><span style="color: #0011ee;"><strong>def ,var, val, lazy val </strong></span>
<br /><div class="quote_title">FX 写道</div><div class="quote_div">val a = e的话,e是会求值的
<br />lazy val a = e的话,e是等到第一次要使用a的值时才求值
<br />def就是使用多少次都重新求值
<br />var是变量
<br /></div>
<br />补充: <pre name="code" class="java">
val a = 3
val a = 4 // ok
a = 2 // 重复赋值,出错</pre>
<br />用得着区分 val 和 def 么 …… 制造这么多词好郁闷 ……
<br />
<br /><span style="color: #0011ee;"><strong>lambda</strong></span>
<br /><pre name="code" class="java">val f = (_:Int) + 1
val f = (s:Int) =&gt; s + 1</pre>
<br />
<br />def 非常微妙 ……
<br />*&nbsp;&nbsp; 加不加括号的区别:
<br /><pre name="code" class="java">def f = {...} // 调用不用加括号,但是不能当 lambda 用
def f() = {...} // 调用必须加括号,可以当 lambda 用</pre>
<br />**&nbsp; 等不等的区别: 没等号相当于返回 Unit 类型。
<br />*** 参数表是逗号分隔还是 )( 分割的区别: tuple 作参数和 curry 化的方法
<br />**** curry 化还可以选择分组,相当赞的设计 !
<br />
<br />*&nbsp;&nbsp; 消灭 var 有好处 —— 引用透明
<br />**&nbsp; 注意消灭了 var 还不够。因为某些 val 的内部状态可以改变。
<br />*** val 可以重新定义,但不能改变值
<br />
<br /><span style="color: #0011ee;"><strong>Nil 和 null</strong></span>
<br /><pre name="code" class="java">scala&gt; null
res14: Null = null
scala&gt; Nil
res15: Nil.type = List()</pre>
<br />
<br /><span style="color: #0011ee;"><strong>curry</strong></span>,结合律的完美诠释
<br /><pre name="code" class="java">f(x,y) == f(x)(y) == (f(x))(y)
// 第一个 == 只对 lambda 有效,对 def 无效,def 是不是 curry 的就看参数表是 tuple 还是分离的括号系列了</pre>
<br />
Scala 的杂记2:class 与 object
一点点编辑器友好的东西: \misc\scala-tool-support\vim
<br />
<br /><span style="color: indigo;"><strong>紧接上回</strong></span>
<br />
<br />将 class 换成 object,就有了 singleton object。
<br /><pre name="code" class="java">object ooxx{
// ...
}</pre>
<br />
<br />覆盖方法得加 override 关键字
<br /><pre name="code" class="java">
class Complex(_re:Double, _im:Double) {
def re = _re
def im = _im
override def toString() = "(" + _re + "," + _im + ")"
// 如果覆盖的不是抽象方法,需要加 override
}</pre>
<br />** abstract, private, extends 关键字用法(基本)同 java
<br />
<br />
<br /><span style="color: indigo;"><strong>case class</strong></span> 提供了打开 class 观察其内容的方法,远胜过繁琐低效的反射和 getter。
<br /><pre name="code" class="java">case class B() extends A // 现在还不管,以后就要强制括号了
case class C(v:Int) extends A</pre>
<br />*&nbsp;&nbsp; 生成实例: B() 和 new B 都可以。
<br />**&nbsp; 对每个构造器参数都自动生成一个 getter 方法。
<br />*** 对象含方法 equals() hashCode() toString()。
<br />
<br />
<br /><span style="color: indigo;"><strong>case 语法</strong></span>(case 语句之间可以用空格/分号/换行分隔) <pre name="code" class="java">o match {
case a1 =&gt; 1
case a2 =&gt; 2
case _ =&gt; n // 下划线匹配任何东西
}</pre>
<br />
<br /><span style="color: indigo;"><strong>case class 和模式匹配范例</strong></span>(from <a target="_blank" href="http://www.scala-lang.org/docu/files/ScalaTutorial.pdf">scala tutor</a>)
<br /><pre name="code" class="java">abstract class Tree
case class Sum (l:Tree, r:Tree) extends Tree
case class Var (n:String) extends Tree
case class Const (v:Int) extends Tree
type Env = String =&gt; Int // 类型别名,类似于 C 的 typedef
def eval (t:Tree, env:Env):Int = t match {
case Sum (l, r) =&gt; eval (l, env) + eval (r, env)
case Var (n) =&gt; env (n)
case Const (v) =&gt; v
}
val t = Sum (Const (12), Var ("x"))
val env:Env = {case "x" =&gt; 3 ;case "y" =&gt; 5}
println ("" + eval (t, env))
</pre>
<br />
<br />*&nbsp; {case ... case ...} 是 (x =&gt; x match{case ... case ...}) 的糖。
<br />
<br />
<br /><span style="color: indigo;"><strong>traits</strong></span> 类似于 haskell 的 class,既可以看做类的类,也可以看做类的特性,还能看做抽象类来 extend。
<br />
<br />范例(from <a target="_blank" href="http://www.scala-lang.org/docu/files/ScalaTutorial.pdf">scala tutor</a>):序关系(this that 真直白)<pre name="code" class="java">
trait Ord {
def &lt; (that: Any): Boolean // 仅声明,lazy 定义
def &lt;=(that: Any): Boolean = (this &lt; that) || (this == that)
def &gt; (that: Any): Boolean = !(this &lt;= that)
def &gt;=(that: Any): Boolean = !(this &lt; that)
}</pre>
<br />*&nbsp;&nbsp; 和 haskell 里面的 Ord class 没差别。可参看 Haskell 类型系统相关文章 和 <a target="_blank" href="http://night-stalker.iteye.com/blog/381756">如何放弃 OOP</a>
<br />**&nbsp; extend 了 Ord 的类,只需要定义 &lt;,就拥有了包括 &lt;= ,&gt;, &gt;= 的所有方法。
<br />*** trait 还可以当做 mixin (当有父类的时候): A extends B with C
<br />
<br />
<br />泛型(原文是个很酷的词: <span style="color: indigo;"><strong>Genericity</strong></span>)
<br />再次 copy and paste ……
<br /><pre name="code" class="java">
// 定义
class Ref[T] {
private var contents: T = _ // 不初始化的东西是 abstract member,所以要用类型 T 对应的默认值(_) 去充满它 ……
def set(value: T) { contents = value }
def get: T = contents
}
// 使用
val v = new Ref[Int]
v.set 3</pre>
<br />
<br />泛型和 trait 结合起来也会很 tricky,作者估计曾经是 C++ 的 TMP(模板元编程)的中毒者 ……<pre name="code" class="java">
trait A[T] {...}
trait B[T &lt;: A[T] {...} // &lt;: 称作 plain bounds,&lt; 体现了 subclass 这个情浓于水的血缘关系</pre>
<br />
<br />*&nbsp;&nbsp; ruby 的继承语法也是用 &lt; 呢
<br />
<br />考虑一个农民工兄弟 java.io.File,它没有 extend 或者 with 过哪个 trait,怎么给他打上小标签,放到轰隆隆的工地上去呢?
<br /><span style="color: indigo;"><strong>view bounds</strong></span> 是对付那些我们没做过手脚的类型的利器。
<br /><pre name="code" class="java">trait Set[A &lt;% T] // A 只要可以隐式转换到 T 就成立,不需要血缘关系</pre>
<br />
<br />有上界 &lt;:,也有下界 &gt;: 。上下界可以用于泛型参数,也可以用于 type 类型声明。
<br />
<br />2 snippets
<br /><pre name="code" class="java">class B[T &lt;: X &gt;: Y]
class A extends B with C with D</pre>
<br />
<br />
<br />-------
<br />
<br />下面这个<span style="color: indigo;"><strong>隐式参数</strong></span>(implicit)的例子很有意思(<a target="_blank" href="http://www.scala-lang.org/node/114">implicit parameter</a>有点像默认参数——如果指定了 implicit 关键字就无效,如果没指定,implicit 将生效并聪明地隐式转换到 implicit object,而且是灰常安全 —— 请称之为 checked 的弱类型!)
<br /><pre name="code" class="java">
// 半群
abstract class SemiGroup[A] {
def add(x: A, y: A): A
}
// 含幺半群
abstract class Monoid[A] extends SemiGroup[A] {
def unit: A
}
// 当编译器遇到 implicit Monoid 时,暗示它:是这个东西哦,是这个东西哦
implicit object StringMonoid extends Monoid[String] {
def add (x: String, y: String): String = x concat y
def unit: String = ""
}
// 是那个东西哦,是那个东西哦
def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
if (xs.isEmpty) m.unit // 这个 if else 的缩进表明作者吃过很多 banana
else m.add (xs.head, sum (xs.tail))
// 使用
sum (List ("a","b","c"))
</pre>
<br />
<br />隐式转换可用于模拟 open class 的效果。几种隐式转换的用法总结:
<br /><pre name="code" class="java">
// 方法 1:如上
// 方法 2:调用 new_method 时,自动用 ImConv 将 Klass 的对象转成子类再调用 ..
implicit def ImConv(o:Klass) = new Klass{
def new_method ...
}
// 方法 2.5:有时不用或者不能子类化,new 一个 Object 就行了
implicit def ImConv(o:Klass) = new {
def new_method ...
}
// 方法 3:令 Klass 可以转换到 WrapKlass
class WrapKlass(k:Klass) {
def new_method ...
}
implicit def imConv(o:Klass) = new WrapKlass(Klass)
</pre>
<br />试了下 implicit class,出错:
<br /><strong>`implicit' modifier can be used only for values, variables and methods.</strong>
<br />
<br />方法 1、2 需要子类化,碰到 final class 就完蛋了。尝试一下 implicit object ss extends String{...} 出错:
<br /><strong>illegal inheritance from final class String</strong>
<br />想要对 final class 下手,就得包装而非子类化,即方法 2.5、3。
<br />
<br />
<br />光暗示是不够的,<span style="color: indigo;"><strong>表白</strong></span>(explicitly typed self references)也很重要!
<br /><a target="_blank" href="http://www.scala-lang.org/node/124">http://www.scala-lang.org/node/124</a>
<br /><pre name="code" class="java">
class B extends C{
self : A =&gt; // 本行表示 this 的类型是 A
def abc = blabla(this) // blabla 可能只接受 A 类型而不接受 B 类型
}</pre>
<br />ETSR 的用处是:当 A 只声明了但还没定义而且下面的定义 B 也可以多态到 A 时,避免编译出错 ……
<br />
<br />
<br />---------
<br />
<br />*&nbsp;&nbsp; 官网文档里这组<a target="_blank" href="http://www.scala-lang.org/node/104"> 语言特性小文章 </a>,适合我们这些专吃蓝蓝路快餐的 ……
<br />
<br />**&nbsp; 一点点代码风格的思考: like haskell,函数和参数之间留个空格大概会比较好?
<br /> see <a target="_blank" href="http://www.iteye.com/problems/20678">http://www.iteye.com/problems/20678</a> (老庄大概会对馆里猿擅自把问题挪到问答频道感到很愤怒 …… )。
<br />
<br />*** 类型的其它 topics
<br />协变、反变
<br />forSome
<br />Null 为可以成为 null 的类型
<br />Nothing &lt;: Null &lt;: {ScalaObject, java类型} &lt;: AnyRef &lt;: Any
<br />Nothing &lt;:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AnyVal &lt;: Any
<br />Type Erasure
<br />Polymorphic Method Types
<br />
<br />
Scala 的杂记3:lift hello world
lift 自从号称比 rails 快 4 倍后,最近又号称比 rails 快 6 倍,这是什么概念呢?
<br />如果大家都不用 cache 不读数据库,scala 比 ruby 快 10 倍才对 ……
<br />如果都用相同 cache 都连数据库,能快 20% 就恭喜了 —— 弄清楚 web 的瓶颈先。
<br />再说部署惯例,apache 得比 lighttpd 慢多少 ……
<br />这个宣传语太浮躁了。
<br />
<br />lift 重启服务器耗时比 rails 慢很多,慢不止 6 倍 …… 修改代码后,不像 rails 那样能马上看到结果,沉重的硬伤啊! 对快速开发影响太大了。
<br />
<br />发完牢骚,lift 还是有它的优点的。
<br /><span style="color: indigo;"><strong>hello world</strong></span> 达人再次出动。
<br />标准的复制自 <a target="_blank" href="http://wiki.liftweb.net/index.php/HowTo_start_a_new_liftwebapp">wiki</a> 的代码,不完全了解参数的意思 ……
<br /><pre name="code" class="console">mvn archetype:generate -U -DarchetypeGroupId=net.liftweb -DarchetypeArtifactId=lift-archetype-blank -DarchetypeVersion=1.0 -DremoteRepositories=http://scala-tools.org/repo-releases -DgroupId=demo.helloworld -DartifactId=helloworld -Dversion=1.0-SNAPSHOT
</pre>
<br />
<br />经过十几分钟到半小时后,helloworld 目录出现了
<br />(可恶的 maven,各种下载 …… 幸好建好工程后可以把它换掉)
<br />
<br />** 和 rails 比比: rails helloworld
<br />
<br />转入 helloworld 目录,结构如下
<br /><pre name="code" class="ruby">
src/
main/
resources/
webapp/ # view 目录,其中的 html 用了 scala 的 schema,文件需要在 boot 配置
index.html
scala/
bootstrap.liftweb/ # 我作了一点修改,用 . 分割的目录名减少嵌套
boot.scala # 启动类
demo.helloworld/ # 同样做了修改
comet/ # 这个是精髓,不过现在还没用到
view/ # 骗人的目录 ……
snippet/ # 相当于 rails 的 helper,scala 的 xml 语法,写起来挺好看
model/
test/ # 测试目录
target/ # 编译目标
pom.xml # 月之女祭司</pre>
<br />
<br />运行 mvn jetty:run, 再经过十几分钟到半小时的下载编译鼓捣,访问 localhost:8080 就能看到页面了,可喜可贺!
<br />
<br />东西都下过一遍后,再次启动服务器需要的时间会大大减少。
<br />lift 默认是不用连接数据库也能跑的,简单修改 Boot.scala ,包含数据库连接和一个新页面 hoho(hoho.html 请放置于 webapp 目录中。)
<br /><pre name="code" class="java">package bootstrap.liftweb
// 那些满屏 copy and paste import 的好好反省下吧!
import net.liftweb._
import util._,
http._,
sitemap._,
sitemap.Loc._,
mapper._
import Helpers._
import java.sql._
object connectionManager extends ConnectionManager {
def newConnection(name : ConnectionIdentifier) = {
try {
Full(DriverManager.getConnection(
"jdbc:mysql://localhost:3306/lift_hello_db",
"root", "root"))
} catch {
case e : Exception =&gt; e.printStackTrace
Empty
}
}
def releaseConnection(conn : Connection) {
conn.close
}
}
class Boot {
def boot {
// where to search snippet
LiftRules.addToPackages("demo.helloworld")
// Build SiteMap
val entries = List(
Menu(Loc("home", List("index"), "go to home")),
Menu(Loc("hoho", List("hoho"), "go to hoho")))
LiftRules.setSiteMap(SiteMap(entries:_*))
// Connect DB
DB.defineConnectionManager(DefaultConnectionIdentifier, connectionManager)
}
}
</pre>
<br />
<br />代码参考了 tutor 的工程,构建时还得把 mysql 的驱动补上,明天继续干。
Scala 的杂记4:skcirT
本篇收集一些想到或者碰到的技巧。
<br />
<br /><pre name="code" class="">-----</pre>
<br /><span style="color: indigo;"><strong>更多的模式匹配</strong></span>
<br />除了可以用 case class 进行匹配以外,还能利用 tuple 和 list 做匹配。
<br />用 list 的情况相当于 case class 的构造函数匹配,同理对变参的 case class 都有效。
<br />
<br /><pre name="code" class="java">def ab = (1, 2)
val (a, b) = ab</pre>
<br />
<br /><pre name="code" class="java">def xxs = List(1, 2, 3)
val x::xs = xxs
val List(a,b,c) = xxs // 此时左右的个数要相等,否则会跳出一个 match error</pre>
<br />
<br />或运算
<br /><pre name="code" class="java">case 1 | 2 | 3 =&gt; ...</pre>
<br />
<br />用 _* 匹配 0 个到多个,产生的结果是 Seq[] 类型
<br /><pre name="code" class="java">val Array(a, b, _, _, c @ _*) = Array(1, 2, 3, 4, 5, 6, 7)
// a:Int = 1
// b:Int = 2
// c:Seq[Int] = Seq(5,6,7)</pre>
<br />
<br />小心 trap:如果要匹配参数 y,必须用 `y` (用反单引号引起来)
<br /><pre name="code" class="java">def f(x:Int, y:Int) = x match {
case `y` =&gt; print("`y`")
case y =&gt; print("y")
}
f(1,1) // =&gt; `y`
f(1,2) // =&gt; y</pre>
<br />但是,如果这个量首字大写,就可以省略掉 `` ……
<br /><pre name="code" class="java">val Foo = 3
val Bar = 4
4 match {
case Foo =&gt; "Foo"
case Bar =&gt; "Bar"
}
// =&gt; "Bar"</pre>
<br />
<br />使用模式匹配的函数例
<br /><pre name="code" class="java">val fac :Int =&gt; Int = {
case 0 =&gt; 1
case n =&gt; n * fac(n - 1)
}</pre>
<br />
<br />使用模式匹配做简单的反射<pre name="code" class="java">
o match {
case that:A =&gt; ...
case that:B =&gt; ...
}</pre>
<br />
<br />case 语句的 guard
<br /><pre name="code" class="java">case ... if ... =&gt; ...</pre>
<br />
<br />match 语句如果没有匹配,就会弹出 match error: x
<br />
<br /><pre name="code" class="">-----</pre>
<br /><span style="color: indigo;"><strong>内部 import</strong></span>
<br />在某个局部作用域内 import 也是可以的,此 import 不影响作用域外面,推荐。
<br />import 还有很多花样,示例:
<br /><pre name="code" class="java">
import java.util.{Set,Map}
import java.io.File, http._ </pre>
<br />
<br /><pre name="code" class="">-----</pre>
<br /><span style="color: indigo;"><strong>运算符</strong></span>
<br />运算符分为 多元中缀,一元前缀,一元后缀。
<br />任何方法都可以当成中缀或者后缀运算符使用。
<br />普通方法当成后缀运算符时,可能会有词法解析的问题,建议使用时在行末加分号,并且在对象和运算符之间留一个空格。
<br />
<br />和 haskell 一样,前缀运算符仅限于 + - ~ !
<br />定义前缀运算符时,需要在定义的方法前加 unary_ ,例如:
<br /><pre name="code" class="java">class A{
def unary_+ = 3
}
+ new A // res4: Int = 3</pre>
<br />
<br /><pre name="code" class="">-----</pre>
<br /><span style="color: indigo;"><strong>Automatic Type-Dependent Closure Construction</strong></span>
<br />在 <a target="_blank" href="http://night-stalker.iteye.com/blog/431865">杂记 2</a> 中写过,单独的一个 {} 是可以看成 (x =&gt; {}) 的,这样就能实现类似于 ruby 的吊尾 block 和实现新的控制结构了。
<br />譬如实现一个 unless 结构
<br />(from <a target="_blank" href="http://www.scala-lang.org/node/138">http://www.scala-lang.org/node/138</a>):
<br /><pre name="code" class="java">
// 类型 【=&gt; Boolean】表示任意返回 Boolean 的 lambda,注意冒号后面要空一格
def unless(cond: =&gt; Boolean)(body: =&gt; Any) =
if (!(cond)) body
</pre>
<br />
<br /><pre name="code" class="">-----</pre>
<br /><span style="color: indigo;"><strong>解释执行不同于编译执行</strong></span>
<br />第一个不同的地方,参看 <a target="_blank" href="http://night-stalker.iteye.com/blog/431341#comments">杂记 1 里 FX 的回帖</a>
<br />
<br />脚本中的最外围空间是可以运行代码的,譬如 杂记 1 中的 hello world
<br />
<br />scala 用作脚本语言的话,可能占用资源有点问题 ……
<br /><a target="_blank" href="http://www.codecommit.com/blog/scala/scala-as-a-scripting-language">http://www.codecommit.com/blog/scala/scala-as-a-scripting-language</a>
<br />
<br />脚本的运行速度也比较慢(启动比 ruby 慢多了 ……)
<br />
<br />非脚本中,最外围空间是不能进行表达式求值的(error: expected class or object definition)
<br />主程序的结构得写成
<br /><pre name="code" class="java">object Xxx extends Application {
// stuffs here ...
}</pre>
<br />或者
<br /><pre name="code" class="java">object Xxx {
def main(args:Array[String]) {
// stuffs here ...
}
}</pre>
<br />编译与运行
<br /><pre name="code" class="console">scalac s.scala
scala -classpath . Xxx</pre>
<br />*&nbsp; 如果要设置编译目标目录: scalac -d tmp
<br />** extends Application 可能有些问题,参见
<br /><a target="_blank" href="http://blogs.sun.com/navi/en_US/entry/scala_puzzlers_part_1">http://blogs.sun.com/navi/en_US/entry/scala_puzzlers_part_1</a>
<br />
<br />Application 后面这个 block 非常神奇,猜想 Application 接收一个 =&gt; Unit 类型的构造参数,Application 定义的 main 便是调用此参数。
<br />试着写一个:
<br /><pre name="code" class="java">abstract class Application2(mian: =&gt; Unit) {
def main(args:Array[String]) {
mian
}
}
object s extends Application2 {
printf("hi")
}</pre>
<br />
<br /><pre name="code" class="">-----</pre>
<br /><span style="color: indigo;"><strong>val 等语句的限制</strong></span>
<br />ruby 和 javascript 可以这样:
<br /><pre name="code" class="ruby">a = b = 3</pre>
<br />但是 scala 的定义语句和赋值语句不一样,而且赋值语句不返回值(或者说返回 Unit)
<br />想到的写法如下 …… 还不如不用连等 ……
<br /><pre name="code" class="java">
val a = {val b = 3; b}
var c = 0
var d = 0
c = {d = 3; d}
</pre>
<br />
<br /><pre name="code" class="java">
var a=0, b=0 // error - -
</pre>
<br />
<br />补充:原来应该这样写 - -,谢谢 EastSun 同学 ~
<br /><pre name="code" class="java">val a, b = 0</pre>
<br />
<br /><pre name="code" class="">-----</pre>
<br /><span style="color: indigo;"><strong>注释也得小心</strong></span>
<br />/* ... */ 起作用优先于 //
<br />而 /* ... */ 可以嵌套,嵌套时 /* 和 */ 必须匹配
<br />所以下面几个都会出错
<br />1.
<br /><pre name="code" class="java">/* comment
/*
*/</pre>
<br />2.
<br /><pre name="code" class="java">/* comment
//*/</pre>
<br />3.
<br /><pre name="code" class="java">/* comment
// */ comment
*/</pre>
<br />
<br />正确的是
<br />1.嵌套匹配:
<br /><pre name="code" class="java">/* comment
/*
*/
*/</pre>
<br />2.加个空格,以免第二行被认成 /* :
<br /><pre name="code" class="java">/* comment
// */</pre>
<br />3.注意注释起作用的顺序:
<br /><pre name="code" class="java">/* comment
*/ // comment</pre>
<br />
Scala 的杂记5:for comprehension, stream, 进程
<span style="color: indigo;"><strong>for comprehension</strong></span> 是一种 list comprehension。通过指定条件(for),产生(yield)的结果是一个 List。
<br />
<br /><pre name="code" class="java">
for(val i &lt;- List.range(1,5); val j &lt;- List(3,5); i &lt; 3; j &gt; 2)
yield i+j
// =&gt; List[Int] = List(4, 6, 5, 7)</pre>
<br />
<br />从其他语言理解:
<br /><pre name="code" class="">sql 或者 linq :
for 相当于 from
yield 相当于 select
&lt;- 相当于 in</pre>
<br /><pre name="code" class="">
haskell :
&lt;- 相当于 ∈
for (ooo) yield (xxx) 相当于 [xxx | ooo]
</pre>
<br />
<br />有一点比较囧:for 条件如果是多行,就不能用圆括号而要用花括号 ……
<br /><pre name="code" class="java">for(val i &lt;- List.range(1,5)
val j &lt;- List(3,5)) // error
yield i+j
for{val i &lt;- List.range(1,5)
val j &lt;- List(3,5)} // ok
yield i+j
</pre>
<br />
<br />scala 编译器会把 for comprehension 翻译成 map、flatMap、filter 的表达方式。
<br /><pre name="code" class="java">
// 如
for (val x &lt;- e; x &gt; 3) yield x+2
// 会翻译成
e.filter(_&gt;3).map(_+2)</pre>
<br />
<br />如果不需要结果,可以用 for-loop
<br /><pre name="code" class="java">for(...){...}</pre>
<br />
<br /><span style="color: darkblue;"><strong>Stream - lazy list</strong></span>
<br />eager 的计算会带来一些效率问题,譬如寻找 1000...10000(不含10000) 中的第一个可以被 13 整除的数:
<br /><pre name="code" class="java">List.range(1000, 10000).filter(_ % 13 == 0)(0)</pre>
<br />这里会先生成一个 List,然后找出 List 里面所有可被 13 整除的数,返回一个新 List,最后取出新 List 的第一个元素 —— 多余工作太多了。
<br />
<br />为了达到 lazy 的计算效果(只有最终结果依赖的内容会被计算),就要用到 Stream 了:
<br /><pre name="code" class="java">Stream.range(1000, 10000).filter(_ % 13 == 0)(0)</pre>
<br />
<br />Stream 的方法大致和 List 相同,只是
<br />用 Stream.cons 代替 ::
<br />用 Stream.append 代替 :::
<br />
<br />Stream 是 lazy 的,做 filter 或者 map 之类的事情时,内容不会马上被计算,当要取出值或者 toList 的时候才开始计算,需要的内容计算完毕以后,马上停止迭代。
<br />
<br />
<br /><span style="color: indigo;"><strong>进程控制</strong></span>
<br />
<br />2009.8.19 补充: io.Source 真好用。
<br />
<br />开启新进程和调用 shell 命令比较繁琐 …… 一旦阻塞,按 ctrl+break 都打不断,只能把 shell 杀掉。
<br /><pre name="code" class="java">
val pr = Runtime.getRuntime exec "java -help"
pr.waitFor() // 等待执行结束
io.Source.fromInputStream(pr getInputStream).getLines.mkString
</pre>
<br />
<br />*&nbsp; 交互式解释器里 resXX 是可以直接用的
<br />*&nbsp; exec 的第二个参数是环境变量,为字符串数组,格式为 "key=val"
Scala 的杂记6:actors
Scala 除了模式匹配和强大的下划线以外,另一个闪光点就是并发模型 Actor。
<br />
<br />Actor 是一种响应消息的对象,处理消息的时候,它可以:
<br /><ul>
<li>改变自身状态
</li><li>产生新的 Actor
</li><li>发送新的消息
</li></ul>此过程可能是异步的,也可能是同步的。
<br />和 Thread 类有点相似吧? scala 的 actor 的实现里,有个私有类 ActorProxy 就是包装线程对象的。但是 Actor 比线程更细小:譬如产生两个 Actor,可能只需要 1 个线程。事实上 Scala 的 Actor 在初始化时,创建一个含 4 个 Thread 的线程池,如果所有线程都阻塞了,线程池会增长。
<br />
<br />Actor 的历史和 message passing 和 process calculi 有些关系。(见 <a target="_blank" href="http://en.wikipedia.org/wiki/Actor_model">wiki</a>)
<br />
<br /><span style="color: indigo;"><strong>message passing,或者说 messaging</strong></span>
<br />C++ 和 java 与其说是面向对象语言,不如说是面向类的语言。
<br />Alan Kay 说过:<a target="_blank" href="http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html">messaging 比 OO 更本质</a>。
<br />C++ 式的成员方法更像是一种语法糖和设计上的 guideline:obj.method() 结果是要被翻译成 method(obj) 的。
<br />而在 messaging 的系统中,中间多了一个消息分发。obj.method() 意味着向 obj 发送 method 消息。
<br />
<br />消息分发的系统更灵活,更具扩展性(譬如一些程序中可以在运行时添加新的消息响应),C++ 和 Java 没有内建的 messaging,于是是人们就想出了类似 signal/slot,event/listener 这些概念 —— 它们本质上都是一个 messaging 的系统。
<br />消息分发的另一个优点就是: 消息可以发给多个对象,对象的响应也可以是异步的。缺点是分发时会有一些额外的开销。
<br />
<br /><span style="color: indigo;"><strong>process calculus</strong></span>
<br />Erlang 的 process 大概就是从 process 代数中来的。 process 代数中,P 和 Q 并行,可以表达为 P|Q,[待续补完 ……]
<br />
<br /><span style="color: indigo;"><strong>actor</strong></span>
<br />actor 的概念表达成三元组的话,就是 (message, mailbox, behavior)。scala 中的实现:
<br /><ul><li>message 一般可以设计成 case class (方便进行匹配)的实例;
</li><li>actor 已经内建了 mailbox (消息队列);
</li><li>behavior 通过 receive 或 react 代码块来设定。</ul>官网 tutor:
</li><a target="_blank" href="http://www.scala-lang.org/node/242">http://www.scala-lang.org/node/242</a>
<br />
<br />基础的用法就像这样:
<br /><pre name="code" class="java">
import scala.actors.Actor
class A extends Actor {
// 实现抽象方法 act: =&gt;Unit
def act {
...
receive {
case Msg1 =&gt; ... // 接收到消息时的处理
// 使用
val a = new A
a.start
// 发送消息
a ! Msg1</pre>
<br />
<br />不过 object Actor 中已经定义了工厂方法 actor(),打开交互式解释器:
<br /><pre name="code" class="java">import scala.actors.Actor._ // 此处导入的是 object Actor 的方法,而不是 class Actor
val a = actor{ receive{
case 1 =&gt; print("received 1")
}}
a ! 2 // 没有定义 2,所以没有响应
// 虽然和 case 没有匹配,但是不会弹出 not match 的异常
a ! 1 // =&gt; received 1 完事了
a ! 1 // 无响应
</pre>
<br />
<br />用 actor() 方法创建的 actor 是立即开始的,从 scala 的源代码中可以找到:
<br /><pre name="code" class="java">def actor(body: =&gt; Unit): Actor = {
val actor = new Actor {
def act() = body
}
actor.start()
actor
}</pre>
<br />
<br />链式响应和循环响应
<br /><pre name="code" class="java">
actor{ react{
case 1 =&gt; react{
case 2 =&gt; print("received 1 and 2"); reply(3)
}
}}
actor{ loop{ react{
case 1 =&gt; ...
}}}</pre>
<br />
<br />带参数的消息可以用 case class 实现,如下面的 Move(x, y)
<br /><pre name="code" class="java">case class Move(x:Int, y:Int)
val a = actor{
react{
case Move(x, y) =&gt; ...
}
}</pre><ul>
<li>!&nbsp; 发送异步消息,没有返回值。
</li><li>!? 发送同步消息,等待返回值。(会阻塞发送消息语句所在的线程)
</li><li>!! 发送异步消息,返回值是 Future[Any]。
</li><li>?&nbsp; 不带参数。查看 mailbox 中的下一条消息。</ul>例子:
</li><pre name="code" class="java">
a !? Move(3, 4) // 注意没有 reply 的话会程序会死掉 ……
a !? (1000, M(3, 4)) // 等待 1000 毫秒等待应答,如果没有 reply,就终止等待
a.!? (1000, M(3, 4)) // 上一行的非操作符写法</pre>
<br />
<br /><ul>
<li>react 和 receive 的 block 里面引用本 actor 时不能用 this —— 要用 self 方法。
</li><li>react 和 receive 区别: react 是 threadless 的,但是不返回值。receive 会返回 case ... =&gt; ... 块的值,但会阻塞 actor self 所在的线程。react 外面不能套 while(true),必须用 loop。
</li><li>带有超时的版本: receiveWithin 和 reactWithin
</li><li>注意 mutable 和 immutable,尽量避免会带来死锁和不稳定问题的 mutable 状态。</ul>
</li>
<br /><span style="color: indigo;"><strong>进一步的参考</strong></span>
<br />
<br />* 避免 mutable state
<br /><a target="_blank" href="http://www.hpl.hp.com/techreports/2009/HPL-2009-148.pdf">http://www.hpl.hp.com/techreports/2009/HPL-2009-148.pdf</a>
<br />
<br />* scala api
<br /><a target="_blank" href="http://scala-tools.org/scaladocs/scala-library/2.7.1/">http://scala-tools.org/scaladocs/scala-library/2.7.1/</a>
<br />
<br />* sbaz 是 scala 的包管理系统。通过 sbaz 安装本地文档:
<br /><pre name="code" class="console">sbaz install scala-devel-docs</pre>
<br />在 %scala_home%\doc\scala-devel-docs\api 可以查看 api
<br />在 %scala_home%\doc\scala-devel-docs\examples\actors 可以找到很多使用 actor 的例子
<br />
<br />* channel 如何工作
<br /><a target="_blank" href="http://en.wikipedia.org/wiki/Actor_model_and_process_calculi">http://en.wikipedia.org/wiki/Actor_model_and_process_calculi</a>
<br />
<br />* remote actor
<br /><a target="_blank" href="http://youshottheinvisibleswordsman.co.uk/2009/04/01/remoteactor-in-scala/">http://youshottheinvisibleswordsman.co.uk/2009/04/01/remoteactor-in-scala/</a>
<br />
<br />* 比较详尽的理论描述(1986, Agha, 扫描版):
<br /><a target="_blank" href="http://dspace.mit.edu/bitstream/1721.1/6952/2/AITR-844.pdf">http://dspace.mit.edu/bitstream/1721.1/6952/2/AITR-844.pdf</a>
<br />
<br />* Scala 作者的 paper:不带控制反转(IoC)的基于事件的编程
<br /><a target="_blank" href="http://lampwww.epfl.ch/~odersky/papers/jmlc06.html">http://lampwww.epfl.ch/~odersky/papers/jmlc06.html</a>
Scala 的杂记7:lift 基础
续<a target="_blank" href="http://night-stalker.iteye.com/blog/432667">杂记 3</a>
<br />
<br />主要是 Exploring Lift 的笔记。
<br />
<br /><span style="color: blue;"><strong><span style="font-size: large;">启动原理</span></strong></span>
<br />
<br /><span style="color: indigo;"><strong>web.xml</strong></span> 的配置(为了少打几个字,用了 yaml 表示 ……)
<br /><pre name="code" class="yaml">
web-app:
filter:
filter-name: LiftFilter
display-name: LiftFilter
description: LiftFilter
filter-class: net.liftweb.http.LiftFilter
filter-mapping:
filter-name: LiftFilter
url-pattern: /*</pre>
<br />
<br />假设你仅仅需要实现一个高性能的简单服务,根本用不着 lift,直接写一个 servlet filter,里面各种 actor 处理并发问题,在 web.xml 里面配上即可 ……
<br />
<br /><span style="color: indigo;"><strong>标准 import</strong></span>
<br /><pre name="code" class="java">
import xml._
import net.liftweb.http._
import S._
import net.liftweb.util._
import Helpers._</pre>
<br />
<br />net.liftweb.http 的成员<pre name="code" class="">
S 用于访问 request, response, cookie, session 等
SHtml 定义 HTML 生成函数
LiftRules 全局设置
</pre>
<br />
<br />bootstrap.liftweb.Boot 默认启动类
<br />最简单的启动类就像这样:
<br /><pre name="code" class="java">class Boot {
def boot = {
LiftRules.addToPackages("your.package")
// 相当于添加了以下几个包:
// your.package.view
// your.package.comet
// your.package.snippet
}
}</pre>
<br />
<br /><span style="color: indigo;"><strong>lift app 的启动过程</strong></span>:
<br />1: url 重写
<br />2: 执行匹配 url 的分发函数
<br />3: 如何找到 view
<br />&nbsp; a: 检查 LiftRules.viewDispatch RulesSeq
<br />&nbsp; b: 如果 a 没匹配,找 template
<br />&nbsp; c: 如果 b 没匹配,找 class
<br />
<br />-----
<br />
<br /><span style="color: blue;"><span style="font-size: large;"><strong>template</strong></span></span>
<br />
<br /><span style="color: indigo;"><strong>template</strong></span> 是 html、xhml、htm 或者没扩展名的文件。命名规则:
<br /><pre name="code" class="">&lt;path to template&gt;[_&lt;language&gt;][.&lt;suffix&gt;]</pre>
<br />其中 language 是按照请求的 accept-language 的规则寻找的
<br />
<br />url /dir/tmpl.xhtml 仅对应同名同位置文件
<br />url /dir/tmpl 对应 /dir/tmpl.xhtml, /dir/tmpl_es-ES.xhtml, ...
<br />
<br />注意:
<br />&nbsp; 1: 摆在 template 根目录的文件不会起作用,如果需要,得手动配置。
<br />&nbsp; 2: /template-hidden 目录中的文件会自动隐藏(所有 -hidden 结尾的目录都会)
<br />&nbsp; 3: 就算 template 的扩展名是 html,都要写成严格的 xml,否则会 parse error(+﹏+)~
<br />&nbsp; 4: template 是动态编译的,修改了不用重启服务器
<br />
<br />-----
<br />
<br /><span style="color: indigo;"><strong>snippet</strong></span> 是 template 中的 lift tag,产生一个 xml 片段。
<br />&lt;lift:Hello /&gt; == &lt;lift:Hello.render /&gt;
<br />&nbsp; == &lt;lift:snippet type="Hello" /&gt; == &lt;lift:snippet type="Hello:render" /&gt;
<br />* 比较长的那种写法,大概是在类名和 surround 等既定标签冲突时用的。
<br />
<br />snippet 遵守递归的,从外到内渲染的规则(用 eager_eval 属性可以改变渲染顺序)
<br /><pre name="code" class="xml">&lt;lift:As.snip&gt;
这里可以访问 As 的 bind,如 &lt;A:name /&gt;
&lt;lift:Bs.snip&gt;
这里可以访问 As 和 Bs 的 bind,如 &lt;A:name /&gt;&lt;B:name /&gt;
&lt;/lift:Bs.snip&gt;
&lt;/lift:As.snip&gt;</pre>
<br />
<br />相应的 snippet 定义:方法签名必须是 public (xml.NodeSeq) =&gt; xml.NodeSeq
<br /><pre name="code" class="java">class As {
def snip (xhtml : NodeSeq) : NodeSeq =
bind("A", xhtml, "name" -&gt; Text("The A snippet"))
}
class Bs {
def snip (xhtml : NodeSeq) : NodeSeq =
bind("B", xhtml, "name" -&gt; Text("The B snippet"))
def snip2 (n: NodeSeq):NodeSeq = &lt;oppai&gt;&lt;/oppai&gt;
}
// 似乎 bind 和返回 xml 的没法放在同一个 snippet 中 ……
</pre>
<br />
<br />-----
<br />
<br /><span style="color: indigo;"><strong>view</strong></span> 也可以拿来当 template(view 和 template 的关系就像 servlet 和 jsp 之间的关系一样)
<br />lift 不是 MVC 框架,可以选择用 view 当 controller,用 template 当 view 来实现 MVC 结构 …… 不过 …… 不折腾了吧 ……
<br />示例来自 exploring lift
<br /><pre name="code" class="java">class ExpenseView extends LiftView {
override def dispatch = {
case "enumerate" =&gt; doEnumerate _
}
def doEnumerate () : NodeSeq = {
...
&lt;lift:surround with="default" at="content"&gt;
{ expenseItems.toTable }
&lt;/lift:surround&gt;
}
}</pre>
<br />
<br />-----
<br />
<br />标签小汇
<br />
<br /><span style="color: indigo;"><strong>snippet: </strong></span>
<br />&nbsp; &lt;lift:snippet form="GET/POST" type="Class:method" multipart="true/false" /&gt;
<br />&nbsp; &lt;lift:Class.method form="...” multipart="..." /&gt;
<br />&nbsp; &lt;lift:Class form="..." multipart="..." /&gt;
<br />
<br /><span style="color: indigo;"><strong>surround:</strong></span>
<br />&nbsp; &lt;lift:surround with="template_name" at="binding"&gt;
<br />&nbsp;&nbsp;&nbsp; children
<br />&nbsp; &lt;/lift:surround&gt;
<br />* 默认 with = "/templates-hidden/default"
<br />* surround 中的 &lt;head&gt; 标签会被 merge 进外头的 &lt;head&gt; 中。
<br />
<br /><span style="color: indigo;"><strong>bind:</strong></span>
<br />&nbsp; &lt;lift:bind name="binding" /&gt;
<br />* 对应 surround
<br />
<br /><span style="color: indigo;"><strong>embed:</strong></span>
<br />&nbsp; &lt;lift:embed what="template_name" /&gt;
<br />* 在一个 template 中包含另一个 template,可用于 Ajax。如果是 js 命令嵌入的 template,子 template 中的 js 不会执行。
<br />
<br /><span style="color: indigo;"><strong>comet:</strong></span>
<br />&nbsp; &lt;lift:comet type="ClassName" name="optional" /&gt;
<br />* 在页面中加入一个 comet actor。name 代表这个 actor 的名字。
<br />
<br />-----
<br />
<br /><span style="color: indigo;"><strong>stateful snippet</strong></span> 是函数式的状态 snippet,比 session 更细粒度。stateful snippet 作用之一是实现多页表单。
<br /><pre name="code" class="java">class MySnippet extends StatefulSnippet {
// dispatch 被用来分发请求,它是 var,所以处理请求时可以改变它
val dispatch: DispatchIt = { case "forms" =&gt; first _ } // MySnippet.forms
// 定义一些存储状态的变量
var s = 0
// 每个状态的处理
def first (x: NodeSeq): NodeSeq = {
dispatch = { case "forms" =&gt; second _ }
s = s + 1
&lt;p&gt;first. s = {s}&lt;/p&gt;
}
def second (x: NodeSeq): NodeSeq = {
dispatch = { case "forms" =&gt; third _ }
s = s * 2
&lt;p&gt;second. s = {s}&lt;/p&gt;
}
// ... and so on
}</pre>
<br />
<br />使用:
<br /><pre name="code" class="xml">&lt;lift:MySnippet.forms /&gt;</pre>
<br />
<br />
<br />-----
<br />
<br /><span style="color: indigo;"><strong>eager_eval</strong></span> :让 tag 内容先运行,再运行 snippet 本身。尤其在 embed 的情况下有用(例子摘自 exploring lift)。
<br />
<br />设 formTemplate 定义为
<br /><pre name="code" class="xml">&lt;hello:name /&gt;
&lt;hello:time /&gt;</pre>
<br />那么
<br /><pre name="code" class="xml">&lt;lift:Hello.world eager_eval="true"&gt;
&lt;lift:embed what="formTemplate" /&gt;
&lt;/lift:Hello.world&gt;</pre>
<br />第一步,eager eval 了内含的 embed 标签
<br /><pre name="code" class="xml">&lt;lift:Hello.world eager_eval="true"&gt;
&lt;hello:name /&gt;
&lt;hello:time /&gt;
&lt;/lift:Hello.world&gt;</pre>
<br />第二步,snippet 可以对 hello:name 和 hello:time 标签求值。
<br />
<br />如果不是 eager_eval,最后就产生两个不能求值的标签了 …… embed 和求值顺序尤其要注意,官网示例程序 PocketChange 里就有个页面求值顺序错了,结果显示了一堆 lift 标签 ……
<br />
<br />-----
<br />
<br /><span style="color: blue;"><strong><span style="font-size: large;">url 重写</span></strong></span>
<br />
<br />前面提到 LiftRules 可以配置几乎一切东西,自然包括 url 重写。请求 url 会先按 "/" 分隔,被解析成 List[String],然后进入重写规则中。例子应该比较 self-explain 了 ……
<br /><pre name="code" class="java">LiftRules.rewrite.append {
case RewriteRequest(
ParsePath(List("account",acctName),_,_,_),_,_) =&gt;
RewriteResponse(List("viewAcct"), Map("name" -&gt; acctName))
}</pre>
<br />
<br />包括子域名的重写详解:
<br /><a target="_blank" href="http://blog.getintheloop.eu/2009/5/3/url-rewriting-with-the-lift-framework">http://blog.getintheloop.eu/2009/5/3/url-rewriting-with-the-lift-framework</a>
<br />
<br />
<br />-----
<br />
<br />本地 build lift api doc :(注意:花费时间非常非常的长!)
<br /><pre name="code" class="cmd">
git clone git://github.com/dpp/liftweb.git
cd liftweb
mvn install scala:doc </pre>
<br />
<br />半小时到一小时之后,需要的 jar 差不多了。(反正这个步骤一定会以出错告终)然后
<br /><pre name="code" class="cmd">mvn scala:doc</pre>
<br />
<br />这漫长的 build 中(最后还是出错鸟!),我深深的感受到:maven 不死,scala 没有出头之日 ……
<br />
<br />
<br />-----
<br />
<br />将 lift-1.0 的 jar 复制到到 scala 的 lib 目录中,可以方便打开交互式解释器探索它的 API。
<br />
<br />(假设 helloworld 什么的都搞过了尝鲜了 —— 那么就是下载过 lift 的 jar 了)先切到我的文档的 .m2 目录中:
<br /><pre name="code" class="cmd">cd D:\My Documents\.m2\repository\net\liftweb</pre>
<br />假设 scala 摆在 e:/scala/scala-2.7.5.final。
<br />拷贝的脚本如下(没办法, ruby 做这种事情太顺手了 …… )
<br /><pre name="code" class="ruby">require 'fileutils'
include FileUtils
fs = Dir.glob './**/*.jar'
fs = fs.select{|f|f=~/1\.0/}
fs.each{|f| cp f, 'E:/scala/scala-2.7.5.final/lib'}
</pre>
Scala 的杂记8:反射、bean 和 IO
<span style="font-size: large;"><span style="color: blue;"><strong>反射</strong></span></span>
<br />
<br />scala 本身没有多少反射成分,基本只能用 java 的反射类,有点繁琐 ……
<br />
<br />前面提到,简单的类型检查,用匹配即可:
<br /><pre name="code" class="java">case v: MyClass =&gt; ...</pre>
<br />
<br />对于引用类型(AnyRef),obj.getClass 得到的是 java.utils.Class 类型的对象。
<br />注意: Class.forName 对 scala 类无效;如果要从类型 Ty 出发获得 class 对象,用全局函数 classOf[Ty] —— 对值类型也一样。
<br />
<br />Class 的一些方法:
<br /><pre name="code" class="java">
val k = obj getClass
k newInstance
k getDeclaredConstructors
k getDeclaredFields //=&gt; Array[java.lang.reflect.Field]
k getDeclaredMethods //=&gt; Array[java.lang.reflect.Method]
// 有个默认方法 SomeClass.$tag
// val 成员对应 private 的 field 和 public getter
// var 成员对应 private 的 field 和 public (get/set)ter
k get&lt;Constructors|Fields|Methods&gt;
k isInstance obj //=&gt; true</pre>
<br />
<br />在交互式 shell 中探索 Method:
<br /><pre name="code" class="java">import java.lang.reflect._
classOf[Method].getMethods.map(m =&gt; println(m.getName))</pre>
<br />
<br />对于任意类型(Any 包括 AnyRef 和 AnyVal,AnyVal 包括 Boolean, Byte, Char, Double, Long, Float, Int, Short, Unit),有以下方法
<br /><pre name="code" class="java">isInstanceOf[T]
asInstanceOf[T]</pre>
<br />
<br />进一步参考:JAM 的源码(JAM 是一个从 java 源文件或者类出发,用 velocity 生成 ActionScript 代码的工具)可以处理 .class 文件的类型信息
<br /><a target="_blank" href="http://annogen.codehaus.org/JAM">http://annogen.codehaus.org/JAM</a>
<br />
<br /><span style="font-size: large;"><span style="color: blue;"><strong>Bean</strong></span></span>
<br />
<br />考虑这个简单的 scala 类:
<br /><pre name="code" class="java">class A { var p = "p" }</pre>
<br />生成的 java 类包含一个 private 字段及两个方法: p() 及 p_$eq(),如果是 val,则 setter 方法 p_$eq 是 private 的。
<br />
<br />虽然这两个方法比 getXX setXX 好看一些 …… 但是如果想要和各种使用 bean 的东西(如 hibernate —— oh no ……)交互,可以用一个 annotation 生成 getP() 和 setP():
<br /><pre name="code" class="java">class A {
@reflect.BeanProperty
var p = "p"
}</pre>
<br />另一个方式是用 BeanInfo 注解 class, 注解的类里面,val 对应 reader,var 对应 r/w, def 对应 method。
<br />注意不能在 scala 中用 getP() 和 setP() ……
<br />
<br />scala 的 reader 和 writer 的标准签名比 get/set 好一些 ……
<br /><pre name="code" class="java">p(): Ty
p_=(that: Ty): Unit // 和 ruby 一样的 xxx= 方法真的很难么 …… </pre>
<br />
<br />但问题是 getter 不能和对应的变量重名 ……
<br />
<br /><span style="font-size: large;"><span style="color: blue;"><strong>IO</strong></span></span>
<br />
<br />scala 专有的 IO 方法:Source.fromFile
<br /><pre name="code" class="java">
//读文件, iter 是一个基于字符的迭代器
val iter = scala.io.Source.fromFile("in.txt", "UTF-8")
iter.mkString //全部读取。bom 统一处理成 0xfeff。
import java.io._
import java.nio.channels._
import java.nio._
// 写文件
val f = new FileOutputStream("o.txt").getChannel
f write ByteBuffer.wrap("a little bit long ...".getBytes)
f close
// 复制文件
val in = new FileInputStream("in").getChannel
val out = new FileOutputStream("out").getChannel
in transferTo (0, in.size, out)</pre>
<br />
<br />--------------------------------------------------------------------------------------------------------------------------------------------------------------------
<br />
<br />了解基本原理后,便可自己添加方便的反射方法:
<br />
<br /><pre name="code" class="java">package x
object X {
// AnyRef.methods
implicit def AnyRefX (o: AnyRef) =
new {
def methods =
o.getClass.getMethods.map(_.getName).toList.sort(_&lt;_)
}
// Iterable.puts,此转换的生效优先级高于 Any.puts
implicit def IterableX [T] (o: Iterable [T]) =
new {
private var pcache: T = _
private val uputs = (t: T) =&gt; {
if (t != pcache) {
Console println t
}
pcache = t
}
def puts = o foreach uputs
}
// Any.puts
implicit def AnyX (o: Any) =
new {
def puts = Console println o
}
}
</pre>
<br />
<br />usage:
<br /><pre name="code" class="java">import x.X._
"s".methods puts</pre>
<br />
<br />output:
<br /><pre name="code" class="">charAt
codePointAt
codePointBefore
codePointCount
compareTo
compareToIgnoreCase
concat
contains
contentEquals
copyValueOf
endsWith
equals
equalsIgnoreCase
format
getBytes
getChars
getClass
hashCode
indexOf
intern
isEmpty
lastIndexOf
length
matches
notify
notifyAll
offsetByCodePoints
regionMatches
replace
replaceAll
replaceFirst
split
startsWith
subSequence
substring
toCharArray
toLowerCase
toString
toUpperCase
trim
valueOf
wait
</pre>
<br />
Maven in Yaml
醒醒吧,maven。&nbsp;
<br />yaml 万岁!
<br />
<br />原文需要翻 wall,就转了 ……
<br /><a target="_blank" href="http://www.coderoshi.com/2007/08/maven-less-ugly.html">http://www.coderoshi.com/2007/08/maven-less-ugly.html</a>
<br />
<br />简单的 yaml 到 xml 的转换小工具,运行需要 ruby。
<br />
<br />以下代码与原文略有不同,存为 y2x.rb。
<br /><div class="quote_title">引用</div><div class="quote_div">
<br /><pre name="code" class="ruby">
require 'yaml'
def convert(hash, tabs=" ")
hash.each do |k,v|
k = k.strip # 不要用感叹号哦,k 有可能是 frozen 的
if v.class == Hash
puts "#{tabs}&lt;#{k}&gt;"
convert(v, tabs + " ")
puts "#{tabs}&lt;/#{k}&gt;"
elsif v.class == Array
puts "#{tabs}&lt;#{k}&gt;"
single_name = k =~ /ies$/ ? k.sub(/ies$/, 'y') : k.chop
v.each do |i|
if i.class == Hash
puts "#{tabs} &lt;#{single_name}&gt;"
convert(i, tabs + " ")
puts "#{tabs} &lt;/#{single_name}&gt;"
else
puts "#{tabs} &lt;#{single_name}&gt;#{i}&lt;/#{single_name}&gt;"
end
end
puts "#{tabs}&lt;/#{k}&gt;"
else
puts "#{tabs}&lt;#{k}&gt;#{v}&lt;/#{k}&gt;"
end
end
end
puts &lt;&lt;-PROJ
&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
PROJ
convert YAML.load_file(ARGV[0])
puts "&lt;/project&gt;"
</pre></div>
<br />用法:
<br /><pre name="code" class="console">ruby y2x.rb hello.yaml &gt; pom.xml</pre>
<br />
<br />例:给 lift hello world 改写的 hello.yaml
<br /><pre name="code" class="yaml">groupId: demo.helloworld
artifactId: helloworld
version: 1.0-SNAPSHOT
packaging: war
name: helloworld
repositories:
- { id: scala-tools.org, name: scala-tools, url: "http://scala-tools.org/repo-releases" }
dependencies:
- { groupId: org.scala-lang, artifactId: scala-library, version: 2.7.5 }
- { groupId: org.scala-lang, artifactId: scala-compiler, version: 2.7.5, scope: test }
- { groupId: net.liftweb, artifactId: lift-util, version: 1.0 }
- { groupId: net.liftweb, artifactId: lift-webkit, version: 1.0 }
- { groupId: net.liftweb, artifactId: lift-mapper, version: 1.0 }
- { groupId: javax.servlet, artifactId: servlet-api, version: 2.5, scope: provided }
- { groupId: junit, artifactId: junit, version: 4.5, scope: test }
- { groupId: org.mortbay.jetty, artifactId: jetty, version: 6.1.6, scope: test }
build:
sourceDirectory: src/main/scala
testSourceDirectory: src/test/scala
plugins:
- groupId: org.scala-tools
artifactId: maven-scala-plugin
executions:
- goals: [compile, testCompile]
configuration:
scalaVersion: 2.7.5
- groupId: org.mortbay.jetty
artifactId: maven-jetty-plugin
configuration:
contextPath: /
scanIntervalSeconds: 5
- groupId: net.sf.alchim
artifactId: yuicompressor-maven-plugin
executions:
- goals: [compress]
configuration:
nosuffix: true
</pre>
<br />
<br />和生成的 xml 对比一下:
<br /><pre name="code" class="xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
&lt;groupId&gt;demo.helloworld&lt;/groupId&gt;
&lt;artifactId&gt;helloworld&lt;/artifactId&gt;
&lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
&lt;packaging&gt;war&lt;/packaging&gt;
&lt;name&gt;helloworld&lt;/name&gt;
&lt;inceptionYear&gt;2008&lt;/inceptionYear&gt;
&lt;repositories&gt;
&lt;repository&gt;
&lt;id&gt;scala-tools.org&lt;/id&gt;
&lt;name&gt;scala-tools&lt;/name&gt;
&lt;url&gt;http://scala-tools.org/repo-releases&lt;/url&gt;
&lt;/repository&gt;
&lt;/repositories&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.scala-lang&lt;/groupId&gt;
&lt;artifactId&gt;scala-library&lt;/artifactId&gt;
&lt;version&gt;2.7.5&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.scala-lang&lt;/groupId&gt;
&lt;artifactId&gt;scala-compiler&lt;/artifactId&gt;
&lt;version&gt;2.7.5&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;net.liftweb&lt;/groupId&gt;
&lt;artifactId&gt;lift-util&lt;/artifactId&gt;
&lt;version&gt;1.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;net.liftweb&lt;/groupId&gt;
&lt;artifactId&gt;lift-webkit&lt;/artifactId&gt;
&lt;version&gt;1.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;net.liftweb&lt;/groupId&gt;
&lt;artifactId&gt;lift-mapper&lt;/artifactId&gt;
&lt;version&gt;1.0&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;javax.servlet&lt;/groupId&gt;
&lt;artifactId&gt;servlet-api&lt;/artifactId&gt;
&lt;version&gt;2.5&lt;/version&gt;
&lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;junit&lt;/groupId&gt;
&lt;artifactId&gt;junit&lt;/artifactId&gt;
&lt;version&gt;4.5&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
&lt;artifactId&gt;jetty&lt;/artifactId&gt;
&lt;version&gt;6.1.6&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;build&gt;
&lt;sourceDirectory&gt;src/main/scala&lt;/sourceDirectory&gt;
&lt;testSourceDirectory&gt;src/test/scala&lt;/testSourceDirectory&gt;
&lt;plugins&gt;
&lt;plugin&gt;
&lt;groupId&gt;org.scala-tools&lt;/groupId&gt;
&lt;artifactId&gt;maven-scala-plugin&lt;/artifactId&gt;
&lt;executions&gt;
&lt;execution&gt;
&lt;goals&gt;
&lt;goal&gt;compile&lt;/goal&gt;
&lt;goal&gt;testCompile&lt;/goal&gt;
&lt;/goals&gt;
&lt;/execution&gt;
&lt;/executions&gt;
&lt;configuration&gt;
&lt;scalaVersion&gt;2.7.5&lt;/scalaVersion&gt;
&lt;/configuration&gt;
&lt;/plugin&gt;
&lt;plugin&gt;
&lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
&lt;artifactId&gt;maven-jetty-plugin&lt;/artifactId&gt;
&lt;configuration&gt;
&lt;contextPath&gt;/&lt;/contextPath&gt;
&lt;scanIntervalSeconds&gt;5&lt;/scanIntervalSeconds&gt;
&lt;/configuration&gt;
&lt;/plugin&gt;
&lt;plugin&gt;
&lt;groupId&gt;net.sf.alchim&lt;/groupId&gt;
&lt;artifactId&gt;yuicompressor-maven-plugin&lt;/artifactId&gt;
&lt;executions&gt;
&lt;execution&gt;
&lt;goals&gt;
&lt;goal&gt;compress&lt;/goal&gt;
&lt;/goals&gt;
&lt;/execution&gt;
&lt;/executions&gt;
&lt;configuration&gt;
&lt;nosuffix&gt;true&lt;/nosuffix&gt;
&lt;/configuration&gt;
&lt;/plugin&gt;
&lt;/plugins&gt;
&lt;/build&gt;
&lt;/project&gt;
</pre>
<br />
<br />ps:&nbsp; 看看我的文档里的 .m2 目录,又大了 ……
<br />pss: 同理可以把 web.xml,ApplicationContext.xml,…… 全部搞成 yaml
Scala 的杂记10:扩展基本类型
隐式转换的常用法:
<br />
<br />1.先定义一个 class 或者 case class :K <span style="color: gray;">(like ruby module)</span>
<br />2.再定义一个 object :O,里面包含 implicit def :(C =&gt; K),转换想要扩展的类型 C 到 K。(或者跳过 1 直接 new 匿名内部类)
<br />3.使用的时候,只需 import O._ (包含所有 implicit def),就能对 C 调用 K 的方法了。<span style="color: gray;">(like ruby include module)</span>
<br />
<br />例如基本类型 String,本身没有定义 * 方法,但是可以隐式转换到包含 * 的 scala.runtime.RichString :
<br /><pre name="code" class="java">"abc" * 3 //=&gt; "abcabcabc"</pre>
<br />
<br />以下为练手作品 …… 对基本类型进行一些扩展,加入一些熟悉好用的 API <span style="color: gray;">(like ruby ~)</span>:
<br /><pre name="code" class="java">package ex
import scala.util.matching._
import scala.util.matching.Regex._
// Extension of Int
case class IntEx (i: Int) {
// 5.times(print)
def times (block: (Int =&gt; Unit)) = {
var j = 0
while (j &lt; i) {
block (j)
j = j + 1
}
}
// 4.upTo(8)(print)
def upTo (upper: Int) (block: (Int =&gt; Unit)) = {
var j = i
while (j &lt;= upper) {
block (j)
j = j + 1
}
}
// 8.downTo(5)(print)
def downTo (lower: Int) (block: (Int =&gt; Unit)) = {
var j = i
while (j &gt;= lower) {
block (j)
j = j - 1
}
}
}
// Extension of String
case class StrEx (s: String) {
// substitute first
def sub (re: Regex, other: String) =
re replaceFirstIn (s, other)
// substitute first, block version
def sub (re: Regex) (block: (String =&gt; String)): String =
re findFirstMatchIn s match {
case None =&gt; s
case Some(m) =&gt; re replaceFirstIn (s, block (m.toString))
}
// global substitute
def gsub (re: Regex, other: String) =
re replaceAllIn (s, other)
// global substitute, block version
def gsub (re: Regex) (block: (String =&gt; String)): String = {
val iter1 = s split re.toString elements
val iter2 = re findAllIn s
val sb = new StringBuilder (iter1.next)
while (iter1.hasNext) {
sb append block(iter2.next)
sb append iter1.next
}
if (iter2.hasNext) {
sb append block(iter2.next)
}
sb.toString
}
// scan pattern and do things on match groups
def scan (re: Regex) (block: (String =&gt; Unit)) {
re findAllIn s foreach block
}
}
// Extension of Array
case class ArrayEx [T &lt;% Ordered[T]] (a: Array [T]) {
// val a = Array (3, 2, 1)
// a sort
def sort {
scala.util.Sorting.quickSort(a)
}
}
// just import ex.Ex._ and all implicit conversions will take effect
object Ex {
// can use String as Regex
implicit def String2Regex (s: String) = new Regex (s)
// can use String as StrEx
implicit def String2StrEx (s: String) = StrEx (s)
// can use Array as ArrayEx
implicit def Array2ArrayEx [T &lt;% Ordered[T]] (a: Array [T]) = ArrayEx (a)
// just for test
def main (args: Array [String]) {
println ("abc".sub ("a", "x"))
println ("abac".gsub ("b|c") (_ * 2))
val a = Array("a", "c", "b")
a.sort // Array.sort ~~
println (a.mkString)
}
}
</pre>
<br />
<br />翻看标准库 API 的一些发现 (好东西不少啊):
<br /><pre name="code" class="java">
s matches (regexp: String)
s trim //strip
s toUpperCase //upcase
s toLowerCase //downcase
s(n)
s substring (closedbound, openbound) //do not accept negative number
s contains ('a','b','c')
s startsWith (s)
s endsWith (s)
s * 12
Array("a","c").mkString(",")
</pre>
<br />
<br />** 如果一个方法调用可能对应两个隐式转换,编译不能通过(ambiguous ic)。
<br />
<br />另一种扩展方法是 Companion Object
<br /><div class="quote_title">Thomas Jung @SO 写道</div><div class="quote_div"><pre name="code" class="java">
object Rational{
// 注意方法名称是 apply
def apply(numerator: Int, denominator: Int) = {
def gcd(a: Int, b: Int): Int = if(b == 0) a else gcd(b, a % b)
val g = gcd(numerator, denominator)
new Rational(numerator / g, denominator / g)
}
}
class Rational(numerator: Int, denominator: Int) {
require(denominator != 0)
override def toString = numerator + "/" + denominator
// other methods go here, neither access g
}
val r = Rational(10,200)
</pre></div>
<br />
<br />附: scala.snippets for vim/snipMate (修改自 java.snippets,待完善)
<br /><pre name="code" class="snippet">snippet main
def main (args: Array [String]) {
${1:/* code */}
}
snippet *
/** ${1}
*/
snippet p
println (${1})
${2}
snippet to
.toString
${1}
snippet pr
private
snippet fi
final
snippet ab
abstract
snippet re
return
snippet try
try {
${1}
} catch {
case ${2} =&gt;
${3}
}
${4}
snippet th
throw
snippet im
import
snippet if
if (${1}) {
${2}
} ${3}
snippet el
else {
${1}
}
${2}
snippet ei
else if (${1}) {
${2}
}
${3}
snippet wh
while (${1}) {
${2}
}
${3}
snippet ma
match {
case ${1}
}
${2}
snippet ca
case ${1} =&gt;
${2}
snippet cc
case class ${1}
snippet co
case object ${1}
snippet ob
object ${1:`Filename()`}
snippet cl
class ${1}
snippet tr
trait ${1} {
${2}
}
snippet as
assert (${1})
${2}
snippet scala
// __
// ________ ___ / / ___
// / __/ __// _ | / / / _ |
// __\ \/ /__/ __ |/ /__/ __ |
// /____/\___/_/ |_/____/_/ | |
// | /
</pre>
爬取 lift 1.0 的 API
lift 不提供离线 API 下载,尝试从源码生成未果 …… 一怒之下决定把 scala-tools 的 api 爬下来。
<br />
<br />首先是分析 url,分析后弄出这么个脚本(ruby1.9,在 Posix <strong>不</strong>兼容系统下面跑)
<br /><pre name="code" class="ruby"># coding: utf-8
require 'rest_client'
# download and save
BaseUrl = "http://scala-tools.org/scaladocs/liftweb/1.0/"
def fetch fname
return if fname =~ /http:/ or File.exist? fname
content = RestClient.get BaseUrl + fname
mod = (fname =~ /\.png$/) ? 'wb' : 'w'
File.open fname, mod do |f|
f &lt;&lt; content
end
print '.'
end
# basic dir structure
`mkdir _highlighter _images`
# get misc files
misc = &lt;&lt;ES
./all-classes.css
./content.css
./reset.css
./_highlighter/SyntaxHighlighter.css
./all-classes.js
./content.js
./jquery-1.3.2.js
./_highlighter/shAll.js
./_images/class.png
./_images/object.png
./_images/trait.png
ES
misc.each_line do |f|
fetch f.strip
end
# analyse urls and get html
re = /href="
\.\/(net\/liftweb\/(?:\w+\/)+)
([^"]+)
"/x
['all-classes.html', 'overview.html', 'index.html'].each do |src|
fetch src
File.read(src).scan re do |pkg, klass|
`mkdir #{pkg.gsub '/', '\\'}` unless File.exist? pkg
fetch pkg + klass
end
end
</pre>
<br />
<br />尝试开两个进程发现网速不行,还会被强制关闭连接。下了好久啊。
<br />如果开几个 proxy,每个进程用一个 proxy,应该能快些,可是隔壁的哥们就看不了优酷了 ……
<br />
<br />scala-tools 上面的其它文档也可如法炮制下载。
<br />
<br />ps:比起 mvn scala:doc,貌似爬下来的方法比较节约带宽 orz ……
动态 mixin
打开交互式解释器,先给 Any 定义一个 伪 - Monadic 方法,消灭括号用:
<br /><pre name="code" class="java">implicit def monadicP(o: Any) = new {
def puts = println(o)
}</pre>
<br />
<br />静态 mixin 是在定义时的 with 子句。而<span style="color: indigo;"><strong>动态 mixin</strong></span> 是在 new 语句后面的 with 子句。
<br />
<br />一个简简单单无意义的例子:
<br /><pre name="code" class="java">
class C
trait M { def beMed () = "M!" }
new C with M beMed () puts //=&gt; M!</pre>
<br />
<br />--------------------------------------------------------------------------------------------------------------------------------------------------------------------
<br />
<br />ruby 的 included 可以让 module 在被 mixin 后做些事情,在 scala 里面叫 <span style="color: indigo;"><strong>Dependency Declaration</strong></span>:
<br />
<br /><pre name="code" class="java">trait M {
this: C1 =&gt; "so you are C1" puts
}
class C1
class C2
new C1 with M //=&gt; so your are C1
new C2 with M //没有匹配: illegal inheritance
class C3 extends C1 with M //注意匹配运行的时刻是实例化后
new C3 //=&gt; so your are C1</pre>
<br />
<br />** 试了下 new with M,失败。
<br />
<br />--------------------------------------------------------------------------------------------------------------------------------------------------------------------
<br />
<br />顺带再提火星人类型(<span style="color: indigo;"><strong>structural typing</strong></span>):
<br />
<br /><pre name="code" class="java">def isMartian(p: {def talksMartian: Unit}) = {
"Martian found!" puts
p puts
}
class UnkownCreature { def talksMartian {} }
isMartian(new UnkownCreature)
//=&gt; Martian found!
//=&gt; line28$object$$iw$$iw$$anon$1@19846fd
isMartian("serpent knight")
//=&gt; compile error</pre>
<br />
<br />structural typing 的实现用了反射,某些情况可能会很慢 ...
<br />使用 new ... with ... 可以快一些,但限制在非 final class 中。
<br />final 的情况,就只能靠隐式转换了。
<br />
<br />用 new ... with 重写火星人类型:
<br /><pre name="code" class="java">
trait TalksMartian { def talksMartian: Bool }
def isMartian(p: TalksMartian) = {
"Martian found!" puts
}
class UnkownCreature { def talksMartian {} }
isMartian(new UnkownCreature with TalksMartian)
//=&gt; Martian found!
isMartian(new String("coco princess") with TalksMartian)
//=&gt; compile error
</pre>
<br />
<br />当然代码可以修改一下,让它各种情况都可以运行而不是编译期出错 ……
_why is no more
转自 ycombinator hacker news
<br />
<br /> _why is no more (twitter.com)
<br /> 157 points by Jeremysr 1 hour ago | 98 comments
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />71 points by Jeremysr 1 hour ago | link
<br />
<br />I just noticed that _why apparently deleted his twitter account. I then found that that wasn't all he deleted:
<br />
<br />http://twitter.com/_why http://github.com/why http://whytheluckystiff.net/ http://poignantguide.net/ http://hackety.org/ http://shoooes.net/
<br />
<br />All disappeared...
<br />
<br />For those who don't know who I'm talking about: http://wikipedia.org/wiki/Why_the_lucky_stiff
<br />
<br />---
<br />
<br />That was pretty immature, to delete the repos as well. Sure, nuke your Twitter, who cares. But people might have been depending on the repos; it's a breach of trust IMO.
<br />
<br />Not impressed.
<br />
<br />---
<br />
<br />Let's wait for the circumstances before passing judgement.
<br />
<br />---
<br />
<br />Name some circumstances which justify pulling down a broadly-used open source project like Hpricot. At a certain point it must be admitted that a project has a life beyond its author.
<br />
<br />---
<br />
<br />He could have had his computer, or email hacked.. have to wait to see if we hear from him before we assume he actually cleared all the accounts himself.
<br />
<br />-----
<br />
<br />
<br />1 point by jsonscripter 2 minutes ago | link
<br />
<br />He got owned?
<br />
<br />-----
<br />
<br />
<br />1 point by TheProkrammer 3 minutes ago | link
<br />
<br />That's why we have DVCS these days.
<br />
<br />-----
<br />
<br />
<br />22 points by patio11 36 minutes ago | link
<br />
<br />That was pretty immature, to delete the repos as well.
<br />
<br />They were git repos, right? I am new to git, but my understanding is you pretty much have to type
<br />
<br />git clone --i-dont-want-a-full-copy-of-the-repository-please-screw-me-over-if-the-master-vanishes
<br />
<br />to be in a position where you are "dependent" on the code (i.e. have looked at it, ever) and do not have a full copy of the entire project extending to the mists of prehistory.
<br />
<br />[Edit: apparently the actual command is
<br />
<br />git clone --depth (some number)
<br />
<br />I like my version better.]
<br />
<br />---
<br />
<br />--depth does have some benefits... one of my friends tried to do a 'git svn clone' of his work's svn repo... he gave up after the process had been running for a full 3 or 4 weeks.
<br />
<br />That said --depth doesn't have too much use when it comes to native git repos though. Unless you have a ton of binary files in the repo and don't want the full history of all of them...
<br />
<br />---
<br />
<br />None of _why's projects were that big.
<br />
<br />---
<br />
<br />Right, but the parent post didn't seem limited to only _why's repos.
<br />
<br />-----
<br />
<br />
<br />12 points by noodle 36 minutes ago | link
<br />
<br />assuming that this is a tantrum, yes. not impressed, only really detrimental. at least have the courtesy to leave your stuff as it is for the people who have and will benefit from it, instead of scrubbing it all clean.
<br />
<br />on the other hand, if this is a case of someone managing to crack/guess/etc his passwords and just wants to do some malicious damage, that would be too bad.
<br />
<br />---
<br />
<br />Have a little respect. Unlike most of us here, _why never profited directly on his online celebrity status. If he chose to remove himself from the 'net, it seems only reasonable that he would want to shut down public repositories he managed, if only to avoid the inevitable deluge of complaints about them going stale, requests for commit rights from other people, etc.
<br />
<br />The fact that you would care more about his shepherding of repositories which have been available to everyone else all along speaks volumes to me about the kind of attitudes that probably helped to drive him offline in the first place.
<br />
<br />---
<br />
<br />Yeah I think so too. My initial reaction was that this is his right, which technically I'm sure it is. It does call into question an unspoken rule of open source though, which is you can abandon your stuff if you want, but don't try to remove all traces, even if that is ultimately a futile act.
<br />
<br />---
<br />
<br />OTOH, I have to believe there are many people with copies of that code.
<br />
<br />---
<br />
<br />Agreed; it's really childish.
<br />
<br />---
<br />
<br />Really hope he's ok...
<br />
<br />---
<br />
<br />Google cache is still live:
<br />
<br />http://74.125.155.132/search?client=safari&amp;rls=en-us&amp;...
<br />
<br />Edit: Also, twitter search results:
<br />
<br />http://search.twitter.com/search?q=%40_why
<br />
<br />Notably:
<br />
<br />racheltostring: RT: @_why: programming is rather thankless. u see your works become replaced by superior ones in a year. unable to run at all in a few more. about 21 hours ago from TwitterFox
<br />
<br />---
<br />
<br />programming is rather thankless...
<br />
<br />Maybe he read http://news.ycombinator.com/item?id=771057 in Re: http://news.ycombinator.com/item?id=771013 ?
<br />
<br />---
<br />
<br />Holy shit... _why is why I'm a Ruby developer to this day, especially the Poignant Guide.
<br />
<br />So very sad.
<br />
<br />---
<br />
<br />Same here<img src="/images/smiles/icon_sad.gif" />
<br />
<br />---
<br />
<br />Why the Lucky Stiff has become Why the [deprecated] Stiff. http://twitpic.com/ehkoy
<br />
<br />---
<br />
<br />THE PURGES HAVE BEGUN! SAVE YOURSELVES!
<br />
<br />Seriously though, I hope he's /okay/....
<br />
<br />---
<br />
<br />Good point; hopefully he's not suicidal. Anybody know the guy personally?
<br />
<br />---
<br />
<br />Nobody knows much about _why. He's always been a mystery.
<br />
<br />---
<br />
<br />Maybe he's planning for a big star-spangled blowout and is getting us ready for it? Kind of like if MJ had jumped out of his coffin at the Staples Center to start singing Thriller?
<br />
<br />---
<br />
<br />hacked?
<br />
<br />---
<br />
<br />I managed to dig up a PDF copy of his Poignant Guide to Ruby over here: http://www.ember.co.nz/resources/whys-poignant-guide-to-ruby...
<br />
<br />Anyone who hasn't checked it out yet, do yourself a favour. And for the rest of us, who would like to remember this anonymous creator of cartoon foxes.
<br />
<br />Let's hope it's just a quarter life crisis and that he's back soon. Or that he finds something else that makes him happier than programming.
<br />
<br />---
<br />
<br />http://hacketyhack.net/ also
<br />
<br />This is a sad day.
<br />
<br />---
<br />
<br />http://tryruby.hobix.com/ too. THIS IS BAD!
<br />
<br />---
<br />
<br />Indeed. All languages need an online app like this!
<br />
<br />---
<br />
<br />That's my mission with http://codepad.org/
<br />
<br />---
<br />
<br />Codepad is such an awesome site. I use it all the time to test/assess the results of a little bit of code on its own.
<br />
<br />---
<br />
<br />Maybe you could try http://friendpaste.com<img src="/images/smiles/icon_smile.gif" />
<br />
<br />---
<br />
<br />Rip it up and start again.
<br />
<br />---
<br />
<br />Someone has already edited his Wikipedia page to mention his online disappearance.
<br />
<br />---
<br />
<br />FWIW, I noticed that all of his sites were down for a period sunday night, too. I figured he was just doing something on the server and everything was back up the next day. I didn't check his github or twitter, though.
<br />
<br />---
<br />
<br />Judging by this, it looks like his account was hacked: http://twitpic.com/ehm2h.
<br />
<br />---
<br />
<br />Domains are still up and registered to him...
<br />
<br />Strange indeed.
<br />
<br />&nbsp; Registrar..: gkg.net (http://register.gkg.net/)
<br />&nbsp;&nbsp;&nbsp; Domain Name: WHYTHELUCKYSTIFF.NET
<br /> Created on..............: 03-JAN-2002
<br /> Expires on..............: 03-JAN-2014
<br /> Record last updated on..: 24-DEC-2008
<br /> Status..................: ACTIVE
<br />
<br />&nbsp; Registrar..: gkg.net (http://register.gkg.net/)
<br />&nbsp;&nbsp;&nbsp; Domain Name: HACKETYHACK.NET
<br /> Created on..............: 25-FEB-2007
<br /> Expires on..............: 25-FEB-2010
<br /> Record last updated on..: 24-DEC-2008
<br /> Status..................: ACTIVE
<br />
<br />&nbsp; Registrar..: gkg.net (http://register.gkg.net/)
<br />&nbsp;&nbsp;&nbsp; Domain Name: POIGNANTGUIDE.NET
<br /> Created on..............: 20-NOV-2003
<br /> Expires on..............: 20-NOV-2010
<br /> Record last updated on..: 24-DEC-2008
<br /> Status..................: ACTIVE
<br />
<br />---
<br />
<br />That's really odd. Just yesterday he had picked up on Twitter again after a several-days/weeks hiatus.
<br />
<br />---
<br />
<br />http://redhanded.hobix.com/ too<img src="/images/smiles/icon_sad.gif" />
<br />
<br />---
<br />
<br />:( Sad indeed.
<br />
<br />---
<br />
<br />Couldn't possibly be that he's fed up with Open Source and the ruby community...
<br />
<br />---
<br />
<br />Even if he is, taking down ANY trace of your online presence is highly unusual.
<br />
<br />---
<br />
<br />_why has always been an unusual guy. He has always prized his pseudonymity online, so I'm honestly not entirely shocked to see that he has decided to simply pack it up and move on.
<br />
<br />It's a sad day for all of us, but I hope and trust that he has good reasons for this decision. His contributions to the world of Ruby and programming-as-art will not be soon forgotten.
<br />
<br />---
<br />
<br />Well true, and he likely cultivated his anonymity for just such an occasion...
<br />
<br />His legend will only grow...
<br />
<br />---
<br />
<br />_why was to the ruby community what willy wonka was to chocolate. Maybe, just as the fictional Willy Wonka secluded himself in his chocolate factory for so many years due to competition, _why is simply secluding himself in his programming factory.
<br />
<br />Deliciously sweet chocolates (or libraries/books/hacketyhack/etc.) will hopefully magically still appear.
<br />
<br />Maybe _why is simply going to oompa loompa land to get little helpers.
<br />
<br />---
<br />
<br />Where is Charlie?
<br />
<br />---
<br />
<br />Maybe you're him?
<br />
<br />---
<br />
<br />Well that sort of ruins one's day. I hope everything is alright with him.
<br />
<br />---
<br />
<br />_why just posted a new repo on github: http://github.com/why/lol/tree/master
<br />
<br />EDIT: Weird, the repo description is "wtf adam, are you this slow". Hope he didn't get hacked.
<br />
<br />---
<br />
<br />That does not appear to be the same 'why' account as before. 0 followers, created today.
<br />
<br />---
<br />
<br />Probably some guy just registered the username after _why deleted it.
<br />
<br />---
<br />
<br />Is it just me, or does Ruby tend have these "personalities" more than other communities? Seems like the Ruby community focuses a lot on the "rockstar" mentality. I don't spend any time in it to know for sure, though.
<br />
<br />---
<br />
<br />He was more like ruby's Batman than rockstar
<br />
<br />---
<br />
<br />_Why is more like a fairy tale character than a rock star. What qualities of his persona do you consider to be rock star qualities?
<br />
<br />---
<br />
<br />People liked him because of the quality of the stuff he produced. I personally haven't seen rockstar qualities from him. Most of his work (code/blog) are to the point and of high quality.
<br />
<br />---
<br />
<br />Maybe we just have more interesting people?<img src="/images/smiles/icon_smile.gif" />
<br />
<br />---
<br />
<br />Yeah it does. It's a bit like Linux-land has more interesting personalities than Microsoft-land or how skateboarders tend to be more exciting than WalMart workers.
<br />
<br />---
<br />
<br />Smalltalk had it's share of personalities. I am witness to drama at OOPSLA that would have made for some damn good movie scenes. But all of this predates YouTube, Blogs, Twitter, etc...
<br />
<br />---
<br />
<br />Which is why I love Zed Shaw's response:
<br />
<br />"Hmm, I think it's more possible that _why got hacked than he pulled a me." http://twitter.com/zedshaw/status/3409334842
<br />
<br />---
<br />
<br />Yes it does. Actually it's one of the reasons I like the Ruby community.
<br />
<br />---
<br />
<br />There's a new repository up on his Github: http://github.com/why/lol/tree/master
<br />
<br />Description: wtf adam, are you this slow
<br />
<br />I'm guessing he got hacked.
<br />
<br />Edit: Nevermind, the repo's been deleted.
<br />
<br />---
<br />
<br />Apparently there's no holding period for deleted names on GitHub.
<br />
<br />If he wanted his twitter account to be taken by somebody else, he could have renamed it before deleting it. If you rename a twitter account, the old name is freed up instantly, but if you delete an account, it is held for an unknown period of time.
<br />
<br />---
<br />
<br />Apparently he tweeted this yesterday (quoted from a retweet): "programming is rather thankless. u see your works become replaced by superior ones in a year. unable to run at all in a few more."
<br />
<br />But _why's contributions are largely irreplaceable. Yeah, software evolves and individual projects might be supplanted by others, but his contributions go way beyond simply writing functioning code.
<br />
<br />---
<br />
<br />But _why's contributions are largely irreplaceable.
<br />
<br />Sadly this is not true for anyone.
<br />
<br />---
<br />
<br />I'm not in the ruby world, so perhaps that's why his contributions have largely been outside my sphere and there may be a great deal I'm unaware of. That said, when someone goes and removes their repositories and whatever other traces of their workproduct they have access to (as is their right), it might make people reluctant to use their products in the future (assuming they return).
<br />
<br />---
<br />
<br />_why has never really made "products" -- in fact, as far as I know, he's never made money from any of the stuff he shared online. (Sales of physical copies of the Poignant Guide may be the one exception to this, but IIRC, he priced it exactly at his cost in order to insure the widest possible distribution.)
<br />
<br />---
<br />
<br />There has been some negativity about hpricot lately:
<br />
<br />http://twitter.com/#search?q=Nokogiri%20hpricot
<br />
<br />---
<br />
<br />And hpricot is the only living remnant of _why.
<br />
<br />---
<br />
<br />burn out?
<br />
<br />---
<br />
<br />Apparently he tweeted this yesterday: "programming is rather thankless. u see your works become replaced by superior ones in a year. unable to run at all in a few more."
<br />
<br />ironically he wrote "you" on twitter, and you wrote "u" on hn.
<br />
<br />---
<br />
<br />It's from a retweet I believe, where the retweeter replaced "you" with "u", perhaps to fit it into 140 chars.
<br />
<br />---
<br />
<br />That's terrible, his code and posts always brightened my day.
<br />
<br />---
<br />
<br />The title is inappropriate (without really knowing what happened to _why). Only his online identify is no more!
<br />
<br />---
<br />
<br />I don't know about that. I think _why was his online identity... only sometimes he ventured into the real-world to talk to people.
<br />
<br />So, perhaps it is accurate, since it seems like the online persona has vanished, so in a way _why is no more.
<br />
<br />Maybe the real person needed to escape from the persona... who knows?
<br />
<br />---
<br />
<br />I suspect some sort of burn out. Honestly the guy was a machine. I remember looking at his commit logs for all of his public Github projects and wondering how he managed it. And mind you, that only accounts for code publicly released and doesn't address any private projects he might have been working on.
<br />
<br />---
<br />
<br />I'm on the shoes mailing list and he recently made this announcement:
<br />
<br />Aug 3rd: &lt;quote&gt; Okay, wow, hello, I'm very behind on this list, I hope you will pardon me. I've been away for the summer, taking a break from everything.
<br />
<br />Well, enough of that, time's up: I hope to concentrate strictly on getting Shoes 3 done. While it's disappointing to see what a poor job I've done with Shoes, I am going to try to do what I can to aright the situation by at least getting us some more speed and stability. (As with all of my projects, it's just a toy experiment which is full of an unpredictable amount of both the sweet and bitter.)
<br />
<br />My hope is to release the final version by September 7th. Please, if you want something fixed for Shoes 3, it will need to be filed on github, in the issues section. Yes, I think that will work just great.
<br />
<br />_why &lt;/quote&gt;
<br />
<br />Perhaps this is a publicity stunt to popularize the new and improved Hackety Hack?!
<br />
<br />---
<br />
<br />I noticed that people had been ripping on his code a bit lately... I think the Ruby community's embrace of Nokogiri really hurt the man's feelings.
<br />
<br />_why has been a great contributor to the Ruby community... let's hope he reincarnates himself<img src="/images/smiles/icon_smile.gif" />
<br />
<br />---
<br />
<br />Man - what a bum out. The talk he gave at art + code was awesome and seriously inspired me to focus more on educational material for teaching kids to program. I hope he at least gives a more valid reason for removing everything. I can understand letting something become dormant - but others can take what you have done and be inspired and or build upon it. That is not a slight to you it's just the way a system grows. Like layers of sediment upon rock upon sediment. If I ever discovered that a piece of my code/idea had made its way into something someone else had done I would be stoked not bummed out.
<br />
<br />---
<br />
<br />Wow, one of the few personalities to ever remain truly anonymous on the Internet vanishes in a day. Sad sad day.<img src="/images/smiles/icon_sad.gif" />
<br />
<br />---
<br />
<br />No, his real name has been on HN before.
<br />
<br />---
<br />
<br />Do you have a link for this, as far as I know, no one knows his real name, job, etc.
<br />
<br />---
<br />
<br />What about moot and weev? They're not members of the open source community, but they are notorious.
<br />
<br />---
<br />
<br />moot's identity was revealed. weev I've not seen any proof yet that he's been outed. I'll correct my post.
<br />
<br />---
<br />
<br />I was playing with hackityhack last night, trying to find a way to contribute to it. It's very important to me that young people have an easy way to learn programming and I think he was really on the right track.
<br />
<br />---
<br />
<br />I learned ruby via his blog, via his poignant guide, and via the older edition copy of the pitchaxe which he had hosted on his site. I really hope he's not going into seclusion for good. :-(
<br />
<br />---
<br />
<br />_why got me back into programming and into ruby, first with Try Ruby! in the browser and later Hackety Hack. When I asked if I could use hackety to build a web app, he steered me toward rails. I've used tons of his library, especially hpricot. If you're listening _why; don't stop being generous - don't disappear without a trace.
<br />
<br />---
<br />
<br />Thanks for all the fish, _why!
<br />
<br />---
<br />
<br />I hope he didn't take my extremely down voted, (apparently) not funny, pun ridden comment on this article too seriously. My puns were harmless, I swear. Any comic drawn in programming text immediately makes me think of _why: http://news.ycombinator.com/item?id=771013
<br />
<br />But seriously, reading _why's poignant guide to ruby back in the day was a breath of fresh air coming from Java, and wanting to try something new. His intermingled illustrations and humorous text made a programming book that I actually found enjoyable to read. Also, his contributions in terms of libraries and interesting apps has been awesome.
<br />
<br />_why: My hats off to you fine sir! May you find goodness on your latest adventures!
<br />
<br />---
<br />
<br />but not totally gone on twitter: http://search.twitter.com/search?q=RT+%40_why
<br />
<br />---
<br />
<br />Does anyone have a copy of all the source code he published?
<br />
<br />Specifically, does anyone has a copy of his Potion (http://news.ycombinator.com/item?id=425258) repository somewhere online?
<br />
<br />---
<br />
<br />Very sad. No more chunky bacon??
<br />
<br />I invite everyone to enjoy a video I set to some of his music, and reflect on his priceless contributions.
<br />
<br />http://www.youtube.com/watch?v=YReSQfSBz4k
<br />
<br />---
<br />
<br />Loved his neat work why's poignant guide to ruby though I'm not a ruby guy.<img src="/images/smiles/icon_sad.gif" />
<br />
<br />But why he'd commit a virtual suicide like this?
<br />
<br />---
<br />
<br />The Hpricot wiki is still live on github, and the website is still up too.
<br />
<br />http://wiki.github.com/why/hpricot http://hpricot.com/
<br />
<br />One of the most thoroughly documented library that I have used.
<br />
<br />It's interesting that he gave a talk in 2005 called "A Starry Afternoon, a Sinking Symphony, and the Polo Champ Who Gave It All Up for No Reason Whatsoever" (from his Wikipedia article)
<br />
<br />Anyone know anything about what he said about the Polo Champ in the talk?
<br />
<br />---
<br />
<br />I have a feeling that this disappearing act is part of a ritualized collecting of energies that he needs to release Hackety Hack. He originally meant it to be ready for Art and Code and I have a feeling that many side-projects since then (including Potion) have been produced out of nervous energy misapplied to tasks other than his One True Project.
<br />
<br />His feelings aren't hurt, I doubt that very much.
<br />
<br />---
<br />
<br />camping 4eva!
<br />
<br />---
<br />
<br />:( A sad day.
<br />
<br />---
<br />
<br />我不是 ns,我是 _why
<br />
<br />处理了一下多余字符:
<br /><pre name="code" class="ruby"># coding: ascii-8bit
s = File.read '1.txt'
f = File.open '2.txt', 'w'
s.gsub! /\nreply\n
.*\n
.*\n
.*\n/x, "\n---\n"
f &lt;&lt; s</pre>
<br />
scala 处理 xml
&lt;meta&gt;从 tricks 里面挖出来并小小的增补了一下。&lt;/meta&gt;
<br />
<br />&lt;前言&gt;scala 可以在代码里直接写 xml,xml 会被认为是 scala.xml.Node 或者是 scala.xml.NodeSeq 对象。一般对象也可以用 {} 嵌入 xml 之中。&lt;/前言&gt;
<br />
<br />&lt;正文&gt;
<br />
<br /><span style="color: indigo;"><strong>匹配</strong></span>:
<br /><pre name="code" class="java">val &lt;body&gt;{v}&lt;/body&gt; = &lt;body&gt;&lt;oppai /&gt;&lt;/body&gt;
//=&gt; v: scala.xml.Node = &lt;oppai&gt;&lt;/oppai&gt;</pre>
<br />
<br />
<br />匹配是无视属性的:
<br /><pre name="code" class="java">val &lt;a&gt;{x}&lt;/a&gt; = &lt;a p="hi"&gt;12&lt;/a&gt;
//=&gt; x: scala.xml.Node = 12
// 注: x 不能直接 toInt,得先 toString</pre>
<br />
<br />
<br />还可以用 <span style="color: indigo;"><strong>scala.xml.Elem</strong></span> 来匹配,其构造器是:
<br />(有点 overkill,不如用 xpath,下面会提到)
<br /><pre name="code" class="java">
case class Elem(val prefix: String, // namespace prefix
val label: String, // (local) tag name
val attributes: MetaData,
val scope: NamespaceBinding, // namespace bindings
val child: Node*) extends Node { ... }
</pre>
<br />
<br />
<br />从字符串或者文件 <span style="color: indigo;"><strong>加载</strong></span>:
<br /><pre name="code" class="java">xml.XML loadString "&lt;p&gt;&lt;/p&gt;"
xml.XML loadFile "abc.xml"</pre>
<br />
<br />
<br />完整的 <span style="color: indigo;"><strong>保存</strong></span> 到一个文件:使用 XML.saveFull,其原型是:
<br /><pre name="code" class="java">
saveFull(filename : java.lang.String,
node : Node,
enc : java.lang.String,
xmlDecl : Boolean,
doctype : DocType) : Unit</pre>
<br />
<br />
<br /><span style="color: indigo;"><strong>xpath</strong></span>:(例子取自
<br /><a target="_blank" href="http://burak.emir.googlepages.com/scalaxbook.docbk.html">http://burak.emir.googlepages.com/scalaxbook.docbk.html</a>)
<br /><pre name="code" class="java">
val biblio =
&lt;bib&gt;
&lt;book&gt;
&lt;author&gt;Peter Buneman&lt;/author&gt;
&lt;author&gt;Dan Suciu&lt;/author&gt;
&lt;title&gt;Data on ze web&lt;/title&gt;
&lt;/book&gt;
&lt;book&gt;
&lt;author&gt;John Mitchell&lt;/author&gt;
&lt;title&gt;Foundations of Programming Languages&lt;/title&gt;
&lt;/book&gt;
&lt;/bib&gt;
biblio \ "book" \ "title" // NodeSeq
biblio \\ "title" // List[NodeSeq]
biblio \\ "_" // List[NodeSeq] 所有子结点
biblio.descendant_or_self // List[NodeSeq]</pre>
<br />
<br />
<br /><span style="color: indigo;"><strong>pp</strong></span> 格式化:
<br /><pre name="code" class="java">
val pp = new scala.xml.PrettyPrinter(80, 4) // 行宽 80,缩进为 4
pp formatNode &lt;b&gt;&lt;a/&gt;&lt;/b&gt;
/* 结果是字符串
&lt;b&gt;
&lt;a&gt;&lt;/a&gt;
&lt;/b&gt;
*/
</pre>
<br />
<br />
<br />还有 SAX parsing 什么的个人不太推荐,还有正则表达式呢 ……
<br /><pre name="code" class="java">"""(?&lt;=&lt;a\s+href\s*=\s*")[^"]+(?=")""".r</pre>
<br />
<br />&lt;/正文&gt;
再探讨摆脱 dll-hell ……
今天用 VC 的工具分析了一下 cici 导入的函数
<br /><pre name="code" class="console">dumpbin -imports ext.so &gt; d.txt</pre>
<br />
<br />结果如下
<br /><pre name="code" class="dumpbin">Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file ext.so
File Type: DLL
Section contains the following imports:
msvcr90-ruby191.dll
10010294 Import Address Table
100122BC Import Name Table
0 time date stamp
0 Index of first forwarder reference
21B rb_define_singleton_method
46B rb_scan_args
18E rb_block_given_p
190 rb_block_proc
381 rb_iv_set
4C8 rb_string_value_cstr
1FE rb_data_object_alloc
332 rb_intern
380 rb_iv_get
1CC rb_check_type
330 rb_int2big
1EF rb_const_get
20B rb_define_class_under
4A9 rb_str_new
149 rb_ary_entry
14F rb_ary_new
4AE rb_str_new_cstr
4C9 rb_string_value_ptr
155 rb_ary_push
150 rb_ary_new2
152 rb_ary_new4
3EB rb_num2ulong
43D rb_raise
238 rb_eRuntimeError
3E0 rb_need_block
217 rb_define_module_under
1AE rb_cObject
215 rb_define_module_function
209 rb_define_class
583 rb_yield_values
2D9 rb_funcall
212 rb_define_method
3E9 rb_num2long
USER32.dll
...
MSVCP90.dll
... // STL runtime
MSVCR90.dll
... // C runtime
KERNEL32.dll
...
GDI32.dll
... // graphic functions
gdiplus.dll
...
MSIMG32.dll
...
Summary
4000 .data
4000 .rdata
2000 .reloc
1000 .rsrc
F000 .text
</pre>
<br />
<br />其中 rb_xxxx 是从 msvcr90-ruby191.dll (ruby runtime)导入的函数。
<br />这些函数属于静态导入:
<br />编译器链接的时候,必须通过(导入函数所在的) dll 对应的 .lib 文件查到(导入函数的)地址,才能链接输出可执行文件。
<br />而每个编译器出产的 dll 地址都不同,静态导入严格依赖于链接时的 .lib 文件,并且换掉 dll 就会出问题。
<br />
<br />幸好还有动态导入,关键的 win32 API 是 GetModuleHandle() 和 GetProcAddress()。
<br />前一段时间做了个实验:
<br /><div class="quote_title">引用</div><div class="quote_div">Solve (rather stupid):
<br />Don't use ruby's includes and libs. Use Win32 API
<br />GetProcAddress(hmodule, strFunction) to locate ruby's C-APIs.
<br />
<br />The following is a simple experiment with VC2008 Express and
<br />ruby191-mswin32_90 and ruby191-mingw32:
<br />
<br /><span style="color: blue;">ho.c</span> (ho 只是个无含义的名字)
<br /><pre name="code" class="c">
#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;
void Init_ho() {
HMODULE h = 0;
FARPROC p = 0;
// why they create so many dll names ...?
if(!h) h = GetModuleHandle("msvcrt-ruby191.dll");
if(!h) h = GetModuleHandle("msvcrt60-ruby191.dll");
if(!h) h = GetModuleHandle("msvcrt70-ruby191.dll");
if(!h) h = GetModuleHandle("msvcrt80-ruby191.dll");
if(!h) h = GetModuleHandle("msvcrt90-ruby191.dll");
if(!h) h = GetModuleHandle("msvcrt100-ruby191.dll");
// and many others ...
if(!h) {
printf("failed");
return;
}
p = GetProcAddress(h, "rb_io_check_readable");
// and may others ...
/* if imported functions are the same in ruby1.8 and ruby1.9,
then a cross-version extension ? */
if(p) {
printf("loaded\n");
}
}</pre>
<br />
<br /><span style="color: blue;">ho.def</span>
<br /><pre name="code" class="def">
EXPORTS
Init_ho</pre>
<br />
<br />building commands are (do not use extconf.rb)
<br />(为了不链接到 ruby runtime,不使用 extconf ... make 的编译方式,写个批处理编译)
<br /><pre name="code" class="bat">
cl -c ho.c
cl -LD -MD -Feho.so ho.obj user32.lib -link -def:ho.def</pre>
<br />
<br />some explanations of the commands:
<br />-LD : link dll
<br />-MD : statically link
<br />-Fe : output file name
<br />-link -def:ho.def : make entry point Init_ho searchable for ruby
<br />
<br />require 'ho.so' will simply print "loaded" on console.
<br />Both ruby191-mswin32_90 and ruby191-mingw32 can load it properly.</div>
<br />
<br />设想整一个修改版的 ruby.h,将 import 函数(rb_xxxx)全部改成函数指针声明,然后初始化时动态导入用到的 rb_xxxx,那么编译时就不用链接到 ruby runtime 的 lib,运行时也不依赖于具体的 ruby runtime 了。
<br />
<br />对 %ruby_home%\include 里面的头文件分析了一下,函数名字很工整,应该可以做批量替换 ~
<br />
<br />下面脚本摆在 include 目录里,用于找出名字比较特殊的导入函数:
<br /><pre name="code" class="ruby">dir = File.dirname(__FILE__)
src = '.'
headers = Dir.glob(src + '/ruby/*.h') + Dir.glob(src + '/i386*/*.h')
# return_types = '(VALUE|void|char|int|long|long\slong|float|double)'
return_types = '\w+'
re_decl = /^(?:unsigned\s)?#{return_types}\s\*?(\w+)\(.*\);$/
type_summary = {}
headers.each do |header|
f = File.read header
f.each_line do |l|
next if l =~ /obsolete/
l = l.strip
if l =~ re_decl and $1 !~ /^(rb_|ruby_|st_)/
l =~ /^(\w+)\s/
ty = $1.dup
puts l + '//' + header if ty != 'return'
type_summary[ty] ||= ''
type_summary[ty] += l
end
end
end
require 'pp'
pp type_summary
</pre>
<br />
<br />这些东西基本是上个月整的 …… 过几周待续 ……
<br />
lift rewrite 的小问题
想要将 "/post/12" 映射到 template "/post/show.html?id=12"
<br />
<br />看了 <a target="_blank" href="http://wiki.github.com/dpp/liftweb/about-url-rewriting">about rewrite</a> 之后,添加了一个规则:
<br /><pre name="code" class="java">
LiftRules.rewrite.append {
case RewriteRequest(ParsePath(List("post", id), _, _, _), _, _) =&gt;
RewriteResponse(List("post", "show"), Map("id"-&gt;urlDecode(id)))
}
}
</pre>
<br />
<br />运行发现无限循环 …… rewrite 后的 request 仍然符合 rewrite 的 pattern,所以变成了递归规则。
<br />
<br />无奈修改了 template 的摆放,把 Response 的 template 写成 List("PostShow"),相当痛苦。终于忍不住上 group 去问了一下,原来添加个 if clause 就行了 …… 这下可秀逗了 ……
<br />
<br /><pre name="code" class="java">LiftRules.rewrite.append {
case RewriteRequest(ParsePath(List("post", id), _, _, _), _, _)
if (id matches "\\d+") =&gt;
RewriteResponse(List("post", "show"), Map("id"-&gt;urlDecode(id)))
}
}</pre>
<br />
<br />dpp 的回复更新:
<br />现在的 RewriteResponse 添加了一个签名 apply(List, Map, Boolean),最后一个参数为 true 时,这个规则就是 non-recursive 的。(大概是 lift 1.1M2 或者更晚的版本添加的,至少 1.1M1 的 API 里面没有)
把 windows7 的默认字体改成 XP 的宋体
为什么这样做就不提了 …… (诅咒米拉鸡)
<br />
<br />准备:
<br />&nbsp; 创建个还原点,以防万一玩大了。
<br />&nbsp; 新鲜生猛的 simsun.ttf 一份(从 XP 中拷出来或者放狗下一个)
<br />
<br />步骤:
<br /><strong>1</strong>.先帮 windows7 自带的丑宋体改个有艺术感的名字。
<br />
<br />一开始,你并没有对 simsun.ttc 的控制权。如何夺权呢? 这是一个稻草大富翁的故事,也是一个从1级升到70级的故事 ……
<br />
<br />在 windows\fonts 文件夹里,右点宋体的属性,先直奔 <span style="color: blue;"><strong>[安全]</strong></span> 选项卡,然后点 <span style="color: blue;"><strong>[高级]</strong></span>,弹出新对话框再点 <span style="color: blue;"><strong>[所有者]</strong></span> 选项卡,再点 <span style="color: blue;"><strong>[编辑]</strong></span>。看到了没? 你机器上的字体文件不是属于你的,是属于 TrustedInstaller 的。后悔当初没用 TrustedInstaller 做用户名了吧? 没关系,把所有权改成自己,然后 <span style="color: blue;"><strong>[确定]</strong></span> 之。现在你可以对权限选项卡动手了,点击 <span style="color: blue;"><strong>[更改权限]</strong></span>,把自己的所有权限项都勾上,OK 后在 <span style="color: blue;"><strong>[常规]</strong></span> 选项卡把名字改成 simsun2.ttc 吧,这名字艺术啊!
<br />
<br /><img src="http://dl.iteye.com/upload/attachment/148536/bbb60abd-6999-32ab-9a21-bf2e4500c25d.png" />
<br />
<br /><strong>2</strong>.改名字是为了删掉它 …… 如果不改名字,点删除会说这玩意正在被使用。改完名字后,可以在 Fonts 文件夹里用正当的、官方推荐的、问答频道人人都会复制粘贴回答的手段删之。那些键盘流尤其要注意:<u>这个操作一定要标准</u>,因为这动作是假惺惺的删掉在字体文件夹的显示而不是真枪实弹的删掉它,说删掉只是让你表面上看不见它而不是真正删掉它 —— 而且,更重要的是:我们要的就是这个效果。所以千万不要 del /F /Q 或者 rm -rf !
<br />
<br /><strong>3</strong>.把从 XP 拷过来的 simsun.ttf 改名为 simsun.ttc,然后拖进 Fonts 里面。注意一定要拖放操作! 那些键盘流尤其要注意,<span style="color: orange;"><strong>一定要用鼠标拖放</strong></span>,命令行复制什么的一定会失败,点右键安装什么的一定不会成功 —— 这是微软给你们设的陷阱!
<br />
<br /><strong>4</strong>.注销再登录,把显示属性的各项微软雅黑都换一遍,看看效果:你的 windows7 越来越像 XP 了吧~
<br />
<br /><strong>5</strong>.打开注册表编辑器,在
<br /><pre name="code" class="regedit">HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes</pre>
<br />把 MS Shell Dlg 改成 "宋体",对话框也差不多了。事情还没完,某些高级 UI 都还是雅黑,这个得改 aero 的 mui …… 风险大收益小,就此缩卵吧。
<br />
<br />6.那些键盘流差不多该提出问题了:cmd 怎么显示不了中文了?
<br />打开注册表编辑器,在
<br /><pre name="code" class="regedit">HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts</pre>
<br />添加键值 新宋体 (TrueType)=simsun2.ttc
<br />最后把 cmd 的字体属性改成新宋体 …… 所以说千万别 del 或者 rm 掉换下来这个字体 ……
<br />
<br />最后的最后,尽快重启,就生效了,enjoy it ……
<br />
<br />我没马上重启,蓝屏了 - -
<br /><img src="http://dl.iteye.com/upload/attachment/148554/be9ee9ff-8d77-36ab-aed1-650d39870adb.png" />
<br />
scala 中的 State
<pre name="code" class="java">case class State(var s: String, var ops: Int) {
type Func = String =&gt; String
def &gt;&gt;= (f: Func) = {
s = f(s)
ops += 1
this
}
}
val State(res, ops) = State("ab", 0) &gt;&gt;= (_ * 3) &gt;&gt;= (_ drop 3)</pre>
<br />
<br />允许 var 确实挺方便的 ……
更好的魔法数?
关于快速开方取倒数,有个著名的魔法数
<br /><a target="_blank" href="http://en.wikipedia.org/wiki/Fast_inverse_square_root">http://en.wikipedia.org/wiki/Fast_inverse_square_root</a>
<br />
<br />推导要点归纳一下:
<br />1.用对数将开方变成乘法。
<br />2.wiki 对 σ 怎么来的说得不是很清楚。简单思想就是:
<br />对 x ∈ [0, 1),用斜率等于 1 的直线 y2 = x + σ 来拟合曲线 y1 = log(2, 1+x) 。
<br />原文中用一阶展开式求 σ,我觉得可以取 y1 - y2 的最大和最小值的平均数作为 σ 的最佳值,使得误差限最小。
<br />
<br />先找最大值,在 wolfram alpha 上面搜:
<br /><pre name="code" class="WA">(log(2,1+x)-x) over [0,1]</pre>
<br />点 approximate form 就出来了
<br />
<br />最小值从图线上就能看出是 0,所以新的 σ = 0.0860713 / 2 = 0.04303565
<br />
<br />而魔法数 R = 3.0 * (B - σ) * L / 2
<br />其中 L = 2^(n-1-b) , B = 2^(b-1) - 1
<br />对于浮点数,n = 32,b = 8,代入得
<br />R = 0x5f37bcb6 ,和原文的 0x5f
[scala] 折腾了下 Lucene 3 和 mmseg4j
这些东西如何工作:
<br />IndexWriter 调用 Analyzer 将文章切成以词为单位的 Stream,然后生成索引。
<br />
<br />Lucene 的 API 好丑啊 ……
<br />incrementToken 和 Attribute 什么的简直就不是人能想出来的,为什么就不会用 hasNext() 和 next() ? set Attribute 减少内存分配? 这种优化有没有用都很难说 ……
<br />
<br /><span style="color: gray;">发完牢骚上代码。mmseg4j 的版本是 1.8.2,作者提供的 Analyzer 只支持 Lucene 2.4,所以重写了点。。
<br /></span>
<br />
<br />中文分词分析器:
<br /><pre name="code" class="java">
package whatever
import org.apache.lucene.analysis.tokenattributes._
import org.apache.lucene.analysis.{Analyzer, Tokenizer}
import java.io.Reader
import com.chenlb.mmseg4j.{MaxWordSeg, Dictionary, MMSeg}
/** mmseg4j analyzer for lucene3.0
*/
class MMSegAnalyzer extends Analyzer {
// seg method: max word
lazy val segMethod = new MaxWordSeg(Dictionary.getInstance)
// to make new stream faster (maybe no use)
val termAttrClass = classOf[ TermAttribute ]
val offsetAttrClass = classOf[OffsetAttribute]
// tokenizer cache
var prevTokenizer: MMSegTokenizer = null
override def tokenStream(fieldName: String, reader: Reader) = {
new MMSegTokenizer(reader)
}
override def reusableTokenStream(fieldName: String, reader: Reader) = {
prevTokenizer match {
case null =&gt; prevTokenizer = new MMSegTokenizer(reader)
case _ =&gt; prevTokenizer reset reader
}
prevTokenizer
}
/** tokenizer for lucene 3.0
*/
class MMSegTokenizer(reader: Reader) extends Tokenizer(reader) {
// attributes, use side effect to generate "token stream"
val termAttr = addAttribute(termAttrClass)
val offsetAttr = addAttribute(offsetAttrClass)
val mmSeg = new MMSeg(reader, segMethod)
/** also resets mmSeg
*/
override def reset(reader: Reader) {
super.reset(reader)
mmSeg.reset(reader)
}
/** send tokens(words) to lucene
* @return false if no more tokens, true if sent one
*/
override def incrementToken: Boolean = {
clearAttributes
mmSeg.next match {
case null =&gt; false
case word =&gt;
// send a word to lucene
termAttr setTermBuffer (word.getSen, word.getWordOffset, word.getLength)
offsetAttr setOffset (word.getStartOffset, word.getEndOffset)
true
}
}
} // end inner class
}
object MMSegAnalyzer extends MMSegAnalyzer</pre>
<br />
<br />
<br />block 风格的 IndexWriter
<br /><pre name="code" class="java">
package whatever
import java.io.Reader
import org.apache.lucene.index.IndexWriter
/** lucene 3 support
*/
trait LuceneSupport {
// file system directory store
val luceneIndexPath: String
lazy val luceneIndex = org.apache.lucene.store.FSDirectory open (
new java.io.File(luceneIndexPath)
)
/** for writeIndex block
*/
class IndexWriterProxy extends IndexWriter(
luceneIndex, MMSegAnalyzer, IndexWriter.MaxFieldLength.UNLIMITED
) {
import org.apache.lucene.document.{Document, Field}
/** simpler way to add document
*/
def addDoc(fields: (String, String)*) = {
val doc = new Document
fields foreach {
case ("id", id) =&gt;
doc add (new Field("id", id, Field.Store.YES, Field.Index.NO))
case (k, v) =&gt;
doc add (new Field(k, v, Field.Store.NO, Field.Index.ANALYZED))
}
addDocument(doc)
this
}
}
/** easier way to write index
*/
def writeIndex(proc: (IndexWriterProxy =&gt; Unit)) {
val writer = new IndexWriterProxy
proc(writer)
writer close
}
}
</pre>
<br />
<br />查询略。
<br />
<br />最后给个例子:
<br /><pre name="code" class="java">
import whatever._
object Example extends LuceneSupport {
val luceneIndexPath = "e:/lulu" // 存放 index 的地方
}
Example writeIndex (
_ addDoc (
"id" -&gt; "12",
"content" -&gt; "弱智儿童欢乐多"
) addDoc (
"id" -&gt; "15",
"content" -&gt; "不要浪费人参了"
)
)
</pre>
[聊天产物] Trie in Ruby
<a target="_blank" href="http://wikipedia.org/wiki/Trie">Trie 是什么</a>
<br />
<br />下面代码做的事和 <a target="_blank" href="http://www.iteye.com/topic/336577">用 DFA 实现文字过滤</a> 差不多,只是用 Hash 把表变成树状 —— 那就是 trie 了,空间要省很多。
<br />
<br /><pre name="code" class="ruby"># coding: utf-8
class Trie
def initialize words
@trie = {}
@max_chars = 0
words.each{|word|
sz = word.size - 1
@max_chars = sz if @max_chars &lt; sz
t = word.unpack('U*').inject(@trie){|t, c|
t[c] ||= {}
}
t[0] = true
}
end
# chars starts with one word in trie, shortest match
def starts? chars
chars.inject(@trie){|t, c|
next_t = t[c]
return false unless next_t
return true if next_t[0]
next_t
}
false
end
# one word in str matches one word in trie
def =~ str
str_arr = str.unpack 'U*'
chars = [0, * str_arr[0...@max_chars]]
str_arr[@max_chars..-1].each {|c|
chars.shift
chars.push c
return true if starts? chars
}
false
end
end
# test, should be true, false
t = Trie.new %w[不过 囧死 怪兽]
puts t =~ "不囧死才怪"
puts t =~ "囧不死才怪"
# bench
t = Trie.new File.read('filter.txt', :encoding =&gt; 'utf-8').split
c = File.read('content.txt', :encoding =&gt; 'utf-8')
require 'benchmark'
puts Benchmark.measure {
1000.times{ t =~ c }
}</pre>
<br />以上实现为原理性的,非常简单 naive,非常容易 break,可能非常的不快。而且是 Ruby 1.9 的,改成 1.8 要费些工夫 ……
<br />
<br />一些链接
<br />第一快(经hooopo试验,还弄不了中文):<a target="_blank" href="http://github.com/tyler/trie">http://github.com/tyler/trie</a>
<br />第二快:<a target="_blank" href="http://github.com/kanwei/algorithms/blob/master/lib/containers/trie.rb">http://github.com/kanwei/algorithms/blob/master/lib/containers/trie.rb</a>
<br />
[re: yuan] 简简单单的记录访问 url 的代理服务器
<pre name="code" class="ruby">require 'webrick'
require 'webrick/httpproxy'
s = WEBrick::HTTPProxyServer.new :Port =&gt; 7012,
:RequestCallback =&gt; proc{|req, res| puts "#{req.unparsed_uri}" }
s.start
</pre>
<br />
<br />在浏览器里把代理设置成 127.0.0.1:7012 就行了。
<br />有时需要频繁切换,推荐 ff 小插件 quick proxy。
<br />
<br />RequestCallback 便是访问的 hook,可以用它做很多事情。
[Ruby 1.9] DL 的一个例子:控制扫雷的小旗
扫雷是可以画画做视频的 …… 如:
<br /><a target="_blank" href="http://www.acfun.cn/html/anime/20100103/66326.html">http://www.acfun.cn/html/anime/20100103/66326.html</a>
<br />
<br />原阿婆主用 C# 写的,我用 Ruby 实现了一部分(把像素都设好,控制好 sleep 时间,用 n 倍速运行,配上乐就能做视频了 —— 当然把这么多像素填好的工程很大 ……)。
<br />
<br /><img src="http://dl.iteye.com/upload/attachment/200883/1ac35286-b19a-35af-a701-9ebe5bd6bd98.png" />
<br />
<br />
<br />注:以下代码(Ruby 1.9) 为原理性的,就画一根线。控制扫雷窗口的方法主要是用 DL 调用 win32 API。
<br />注:坐标系基于 win7 的扫雷,click 方法中的 41 便是左上角格子的原点附近,对于 XP 等系统要改小一些。
<br />
<br /><pre name="code" class="ruby"># coding: utf-8
require "dl"
require "dl/import"
include DL
module MineSweeper
extend Importer
dlload 'user32.dll'
# ------------------------------------------------------------------------------
# memory for title match
MSstr = '扫雷'.encode(Encoding.default_external)
Mem = CPtr[MSstr + "\0"]
def Mem.zero
Mem.size.times do |i|
Mem[i] = 0
end
end
# ------------------------------------------------------------------------------
# find minesweeper window handle and assign to $ms_h
# window title text
# (handle, char*, sz)
extern 'int GetWindowText(int, int, int)', :stdcall
# to find minesweeper window
# (proc, lparam)
extern 'int EnumWindows(void*, int)', :stdcall
# (handle, lparam)
EnumCallback = bind "int enum_call_back(int, int)" do |h, _|
Mem.zero
GetWindowText h, Mem.to_i, Mem.size
txt = Mem.to_s.strip.force_encoding Encoding.default_external
if txt == MSstr
$ms_h = h
0
else
1 # 0 stops it
end
end
EnumWindows EnumCallback, 0
exit unless $ms_h
# ------------------------------------------------------------------------------
# activate minesweeper window
extern 'int SetForegroundWindow(int)', :stdcall
SetForegroundWindow $ms_h
# ------------------------------------------------------------------------------
# simulate right clicks
# move cursor
extern 'int SetCursorPos(int, int)', :stdcall
# generate a mouse event
# (event, x, y, _, _)
extern 'void mouse_event(int, int, int, int, int)', :stdcall
MOUSEEVENTF_RIGHTDOWN = 0x0008
MOUSEEVENTF_RIGHTUP = 0x0010
# for coordinate transformation
# (handle, point*)
extern 'int ClientToScreen(int, void*)', :stdcall
POINT = struct ['int x', 'int y']
module_function
# move mouse and right click
def click x, y
x = x*18 + 41
y = y*18 + 41
p = POINT.malloc
p.x = x
p.y = y
ClientToScreen $ms_h, p
SetCursorPos p.x, p.y
sleep 0.01
mouse_event MOUSEEVENTF_RIGHTDOWN, x, y, 0, 0
mouse_event MOUSEEVENTF_RIGHTUP, x, y, 0, 0
sleep 0.05
end
# right click twice to remove the little flag
def unclick x, y
x = x*18 + 41
y = y*18 + 41
p = POINT.malloc
p.x = x
p.y = y
ClientToScreen $ms_h, p
SetCursorPos p.x, p.y
sleep 0.01
mouse_event MOUSEEVENTF_RIGHTDOWN, x, y, 0, 0
mouse_event MOUSEEVENTF_RIGHTUP, x, y, 0, 0
mouse_event MOUSEEVENTF_RIGHTDOWN, x, y, 0, 0
mouse_event MOUSEEVENTF_RIGHTUP, x, y, 0, 0
sleep 0.05
end
end
def slop show
if show
12.times do |i|
MineSweeper.click i, i
end
else
12.times do |i|
MineSweeper.unclick i, i
end
end
end
def hori show
if show
12.times do |i|
MineSweeper.click i, 0
end
else
12.times do |i|
MineSweeper.unclick i, 0
end
end
end
5.times {
slop true
slop false
sleep 2
hori true
hori false
}
</pre>
Sinatra 中的缓存是如此的简单
<p> </p>
<table><tr>
<td>
<p>Ryan Tomayko</p>
<p><span style="font-size: xx-large;"> →</span>
</p>
</td>
<td>
<br><img src="http://dl.iteye.com/upload/attachment/205559/6b50b911-d387-3d7d-976e-a1ed601c7582.jpeg" alt=""><br>
 </td>
</tr></table>
<p> </p>
<p> </p>
<p><span style="text-decoration: underline;"><strong><span style="color: #666699; font-size: medium;">页面缓存</span>
</strong>
</span>
</p>
<p> </p>
<p><a target="_blank" href="http://www.sinatrarb.com/">sinatra</a>
和 rails 的一个区别是 sinatra 默认是多线程模式。(不过当用到非线程安全的库时,可能还需要打开 mutex 选项,用单线程方式执行)</p>
<p> </p>
<table style="padding: 5px; background-color: #faf8e5; margin-left: 12px;" width="695"><tr>
<td>多线程模式搞缓存(cache)要容易一些。单线程的程序往往需要多进程避免并发时的 IO
堵塞,如果每个进程内都缓存一通,消耗的内存就会很大,如果用外部缓存如
memchached,就弄得稍繁琐,而且要考虑接口代码的额外消耗。(不过外部缓存也有优势,可以独立于服务器运行)</td>
</tr></table>
<p><br><br><a target="_blank" href="http://www.sinatrarb.com/one-oh-faq">sinatra 1.0 pre release</a>
包含了 <a href="http://github.com/rtomayko/tilt">tilt</a>
(适配多种模板的视图模块),里面有个很简单的 Tilt::Cache,不过它有点过于简单(譬如不能控制失效时间),自己写一个 cache 类也很容易:</p>
<pre name="code" class="ruby"># coding: utf-8
# Ruby 1.9
# super simple cache control
class Cachee &lt; Hash
# 30 min default
def fetch path, t=nil
# page data, expire time
data, et = self[path]
now = Time.now
if !et or et &lt; now
data = yield
self[path] = [data, now + (t || 1800)]
end
data
end
alias expire delete
# clear expired caches
def sweep
now = Time.now
delete_if do |_, (_, t)|
t &lt; now
end
end
end
</pre>
<p>
<br><br>
其使用和 Tilt::Cache 很相似,如:</p>
<pre name="code" class="ruby">$cache = Cache.new
get '/' do
# expire after 1 hour
$cache.fetch '/', 3600 do
haml :index
end
end
</pre>
<p> </p>
<table style="padding: 5px; background-color: #e4f3f4; margin-left: 12px;" width="695"><tr>
<td>这个机制非常简单:超出时间后,在 fetch 时自动生成新的内容覆盖它。也可主动调用 expire 使其失效。另外 sweep 可清扫 cache,释放一些内存空间。</td>
</tr></table>
<p><br><br>
另一种缓存方式是用 <a target="_blank" href="http://github.com/Sixeight/sinatra-cache">sinatra-cache</a>
,它通过生成静态文件来达成缓存。</p>
<p> </p>
<p> </p>
<p><span style="text-decoration: underline;"><strong><span style="color: #666699; font-size: medium;">浏览器端缓存</span>
</strong>
</span>
</p>
<p><br>
sinatra 支持 http <a href="http://www.powerset.com/explore/semhtml/HTTP_ETag?query=etag">etag</a>
属性 (浏览器端缓存),etag 的作用是:当同一个会话重复请求一个资源的时候,服务器返回 http 代码 304- not changed,省去了文件的传输,页面响应速度要快很多。(sinatra 不对静态文件作 etag 处理,但是 <a href="http://nginx.org/">nginx</a>
, apache 等 web 服务器都默认开启静态文件的 etag。)</p>
<p> </p>
<p>在上面例子中添加 etag 非常的简单:</p>
<pre name="code" class="ruby">$cache = Cache.new
get '/' do
etag 'mimi'
$cache.fetch '/', 3600 do
haml :index
end
end
</pre>
<p> </p>
<table style="padding: 5px; background-color: #fdf3fc; margin-left: 12px;" width="695"><tr>
<td>
<p><a href="http://www.sinatrarb.com/api/index.html">sinatra API</a>
中 etag 的参考</p>
<p><a href="http://www.sinatrarb.com/api/classes/Sinatra/Helpers.html#M000021">http://www.sinatrarb.com/api/classes/Sinatra/Helpers.html#M000021</a>
</p>
<p> </p>
<p>查看源码可以看到,etag 如果发现请求头带有浏览器缓存,就会调用了 halt 304,而 halt 方法包含一个 throw,halt 之后 etag 下面的代码都不会执行。</p>
</td>
</tr></table>
<p> </p>
<p>邮件列表上一个 <a href="http://groups.google.com/group/rack-cache/browse_thread/thread/d845ed2527c6383c/489f3a3d41984b8d?show_docid=489f3a3d41984b8d&amp;fwc=2">讨论</a>
中还提到控制浏览器的缓存时间:</p>
<pre name="code" class="ruby">response['Cache-Control'] = 'public, max-age=3600' </pre>
 
<p>既然 sinatra 管好动态内容的 etag 了,那么静态内容的 etag 也交给相应的专家吧。譬如 nginx 的配置就像这样:</p>
<p> </p>
<pre name="code" class="conf">worker_processes 3;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
gzip on;
# 配置 cluster
# 应保证这些端口上运行 thin 或者 mongrel 服务器
upstream myclusters {
server 127.0.0.1:4001;
server 127.0.0.1:4002;
server 127.0.0.1:4003;
}
server {
listen 80;
server_name localhost;
charset utf-8;
# 主要内容交给 sinatra
location / {
proxy_pass http://myclusters;
}
# 静态内容交给 nginx
location ~ /css {
root myapp/public;
}
location ~ /img {
root myapp/public;
}
location ~ /js {
root myapp/public;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root myapp/public;
}
}
}
</pre>
<p> </p>
<p>在 windows 下也能用,开发服务器也不慢呢。</p>
<p>开两台机器,一台开发,一台看效果,后台跑个<a href="http://www.iteye.com/topic/584011">脚本</a>
每 5 秒点一下浏览器的刷新钮 ……</p>
<p> </p>
<p> </p>
<p><strong><span style="text-decoration: underline;"><span style="color: #666699; font-size: medium;">缓存策略</span>
</span>
</strong>
</p>
<p> </p>
<p>再回来说说页面缓存。</p>
<p> </p>
<p>根据不同的资源特点,选用不同的缓存策略,可以精细的调整程序,获得最佳的性能表现。</p>
<p>譬如一个队列式的,限制大小的缓存:</p>
<p> </p>
<pre name="code" class="ruby"># coding:utf-8
class QueuedCachee &lt; Hash
# sz limits the max size of cache
def initialize max
@max = max
super
end
alias expire delete
alias expire_if delete_if
# fetch cache or (yield and add to cache)
def fetch path
# ruby 1.9 的 Hash 同时也是一个链表 (万能数据结构啊!)
# Hash 里面的元素是按照插入顺序排列的
# 如果已存在,删除,再将其插入最尾端
# 如果不存在,且缓存数量超标,删除最前端(老)的元素,插入 yield 结果
data = expire path
if !data and size() &gt;= @max
oldest, _ = first()
expire oldest
end
self[path] = data || yield
end
# remove oldest n cached items
def sweep n
expire_if do
return if n &lt;= 0
n -= 1
end
end
end
</pre>
<p> </p>
<p>还有其它的策略,如按访问量控制(实现或许需要一个优先队列,用 <a href="http://github.com/kanwei/algorithms">algorithms</a>
能省一点点功夫)。如果需要非常复杂的缓存功能,最好还是用现成的东西 …… 如果应用程序架构设计得好,切分为很多独立的小 sinatra app 和消息服务器,这些小缓存类虽然简单,但也非常够用了。</p>
<p> </p>
<p>ps: 这些缓存类还可以兼任对象缓存、数据库缓存和 fragment 缓存,在静态语言中这几乎是不可能的。</p>
<p> </p>
<p> </p>
<p><span style="text-decoration: underline;"><strong><span style="color: #666699; font-size: medium;">题外:多线程和单线程的组合,scaling</span>
</strong>
</span>
</p>
<p> </p>
<p>处理请求的时候,mongrel 是多线程的,thin 是单线程的。</p>
<p>thin 处理单任务的时候性能优于 mongrel,但是进程开多了内存消耗相对高一些。</p>
<p>可以把线程安全的组件放到 mongrel,再把 mongrel 在负载平衡中的权重设高一点,用细致的调整满足大量并发访问的需求。</p>
<p> </p>
<p>sinatra REST 的优点不仅仅是 "url 比较短",它还体现了横向切分资源的通用设计方法。</p>
<p>每个 sinatra 程序根本复杂不到哪里去。在设计庞大的应用程序的时候,基本套路就是按资源切分成很多简单的独立 sinatra 程序。在扩展升级的时候,把 sinatra 程序放到不同的服务器中,轻松完成 scaling。从出生起就没有 A 依赖 B,B 依赖 C …… 的连锁问题(我在整 lift 的时候被弄怕了 …… play! 那 63 M 的下载包也让人很郁闷)。</p>
<p> </p>
没写过 Scheme 解释器的人生是不完整的
如题! 下面的 scheme 解释器用了本人新造的一个解析器生成器: rsec。
<br />
<br />如何装 rsec:
<br /><pre name="code" class="console">gem install rsec -s http://rubygems.org</pre>
<br /><strong>系统需求 Ruby 1.9</strong>
<br />其中用了大量模式匹配等新特性,没法兼容 Ruby 1.8 了 = .=
<br />
<br />rsec 的详细用法可参考:
<br /><a target="_blank" href="http://hllvm.group.iteye.com/group/topic/17817">http://hllvm.group.iteye.com/group/topic/17817</a>
<br />
<br />
<br />上代码之前先对下面这个浓缩版 <a target="_blank" href="http://groups.csail.mit.edu/mac/projects/scheme/">Scheme</a> 作一些说明。
<br />
<br /><strong>语法说明:</strong>
<br />
<br />scheme 程序是由表(list)组成的,表的元素为 cell,cell 可以是表。
<br />每个表可能有不同的语义,但语法就这么简单,可以直接转化为 AST。
<br />
<br />Scheme#initialize 方法新建了一个 parser,将源代码文本解析成大表套小表的树形结构,然后递归调用 eval 方法(树遍历解释)产生程序运行的结果。
<br />
<br /><strong>语义说明:</strong>
<br />
<br /><span style="color: olive;"><strong>define</strong></span>: 定义一个量,这个量可以是整数,也可以是布尔值(#t 或者 #f),还可以是 lambda
<br />如下面分别定义了 a = 12 和 plus4 = \x -&gt; x+4
<br /><pre name="code" class="scheme">(define a 12)
(define (plus4 x) (+ x 4))</pre>
<br /><span style="color: olive;">
<br /><strong>lambda</strong></span>: 定义一个 <a target="_blank" href="http://en.wikipedia.org/wiki/Lambda_calculus">lambda 表达式</a>。每个 lambda 表达式引入一个局部作用域,局部作用域中定义的量在外面不可见。下面代码中用一个哈希表(见 Bind 类)表示一个层级的局部作用域,其 parent 属性为上一层作用域。
<br />
<br /><span style="color: olive;"><strong>if</strong></span>: 条件跳转语句。用法为
<br /><pre name="code" class="scheme">(if cond true-clause false-clause)</pre>
<br />
<br /><span style="color: olive;"><strong>display</strong></span>: 显示语句。用法为
<br /><pre name="code" class="scheme">(display something)</pre>
<br />
<br />
<br />下面是完整代码:
<br /><pre name="code" class="ruby"># coding: utf-8
require "rsec"
class Scheme
include Rsec::Helpers
# 语法部分,只支持布尔值和整数值
def initialize
bra = /\(\s*/.r.skip
ket = /\s*\)/.r.skip
boolean = /\#[tf]/. r.map {|n| ValueNode[n=='#t'] }
integer = /0|[1-9]\d*/.r.map {|n| ValueNode[n.to_i] }
spacee = /\s*/.r.skip
id = /[^\s\(\)\[\]]+/.r.on {|n|
def n.eval bind, *xs
bind[self]
end
}
atom = boolean | integer | id
list = nil # declare for lazy
cell = atom | lazy{list}
list = ( bra &gt;&gt; cell.join_(spacee) &lt;&lt; ket ).map {|n| ListNode[*n] }
program = (spacee &gt;&gt; cell.join_(spacee) &lt;&lt; spacee).map {|n| ListNode[*n] }
@parser = program.eof
end
# 运行解释器
def run source
# 解析结果(如果 pp 它,可以看到一个 AST)
res = @parser.parse! source
# 运行程序
res.eval Runtime.new
end
# 值结点
ValueNode = Struct.new :val
class ValueNode
def eval *xs
val
end
def pretty_print q
q.text "&lt;#{val}&gt;"
end
end
# (表 结 点)
class ListNode &lt; Array
def eval bind
head, *tail = self
case head
when String
pr = bind[head]
pr.is_a?(Proc) ? pr[bind, tail] : pr
when ListNode
map{|n| n.eval bind }.last
end
end
end
# 作(用(域))
class Bind &lt; Hash
def initialize parent = {}
@parent = parent
end
def [] key
super(key) || @parent[key]
end
# define a function
def define id, &amp;p
self[id] = proc do |bind, xs|
p[* xs.map{|x| x.eval bind }]
end
end
end
# 运行时 -- 顶级作用域
class Runtime &lt; Bind
def initialize
super()
# 语法: 声明一个局部量
self['define'] = proc do |bind, (param, body)|
case param
# (define (name plist[0]) body)
when ListNode
func, *xs = param
self[func] = self['lambda'][bind, [xs, body]]
# (define param body)
when String
self[param] = body.eval bind
end
end
# 语法: 声明一个 lambda 表达式
# (lambda (xs[0] xs[1]) body)
self['lambda'] = proc do |bind_def, (xs, body)|
xs = [xs] if xs.is_a?(String)
new_bind = Bind.new bind_def
# (some vs[0] vs[1])
proc do |bind_call, vs|
vs = vs.map{|v| v.eval bind_call}
body.eval new_bind.merge Hash[xs.zip vs]
end
end
# 语法: 条件判断 (注意这不是一般函数,因为它是 lazy 求值的)
self['if'] = proc do |bind, (p, left, right)|
p.eval(bind) ? left.eval(bind) : right.eval(bind)
end
# 一些杂碎的基本函数
%w|+ - * / ** %|.each do |s|
define s, &amp;s.to_sym
end
define '=', &amp;:==
# 预定义一个 display 来显示结果
define 'display' do |x|
puts x
end
end
end
end
s = Scheme.new
s.run File.read ARGV[0]
</pre>
<br />
<br />用来测试的 hello.scm 如下
<br /><pre name="code" class="scheme">
(define (fact x)
(if (= x 0)
1
(* x (fact (- x 1)))))
(display (fact 6))</pre>
<br />
<br />&gt;&gt;= 运行 =&lt;&lt;
<br /><pre name="code" class="cosole">$ ruby scheme.rb hello.scm
720</pre>
<br />
<br />以上代码用意是显示 rsec 的用法,并不是彻底完整的 scheme 解释器。
<br />当然不支持其它的值类型,macro 等东西 ……
<br />
<br />附链接: Writing a scheme in 15 minutes, treetop 版本
<br /><a target="_blank" href="http://blog.jcoglan.com/2009/05/19/talk-writing-a-language-in-15-minutes/">http://blog.jcoglan.com/2009/05/19/talk-writing-a-language-in-15-minutes/</a>
用 RemoteActor 制作一个弱弱的分布式缓存 - 1
前言:
<br />面试的时候孤城老大问到 actor 怎么个好法,我扯了一通,感觉讲的太虚而且很不清楚 …… 下面几篇文章旨在表达一下 actor 的各种好处,顺便帮助自己理解一下这几天看的一点分布式系统的构建知识。
<br />
<br />
<br /><span style="font-size: large;"><span style="color: indigo;"><strong>传统的方法调用</strong></span></span>
<br /><img src="http://dl.iteye.com/upload/attachment/216251/6c98720e-334e-3d0f-ab10-c7a61245700b.png" />
<br />传统的方法调用,默认行为是同步的,调用者(Caller)需要等待 被调用者(Callee)返回一个值,如果被调用者死锁了,整个程序就都死锁了。修改成异步调用需要新起一个线程,既繁琐又重量级。为保证性能,往往需要用线程池的方式进行调度。
<br />
<br />另外,调用者需要知道被调用者的类型或者接口,产生了依赖关系。
<br />
<br />如果 Callee 是远端的对象,还需要进行大动筋骨的修改(如定制一套 RPC 的协议 ……)。
<br />
<br />
<br /><span style="font-size: large;"><span style="color: indigo;"><strong>Actor 的 “方法调用”</strong></span></span>
<br /><img src="http://dl.iteye.com/upload/attachment/216253/e530c9d6-1eb2-3b5f-8a6a-458685b1d791.png" />
<br />Actor 的“方法调用”通过发送消息来实现。发送消息后可以选择干别的事情(异步调用)或者等待返回值(同步调用)【注1】。Actor 比线程更细更轻量,Scala 的 Actor 后端已经是一个线程池了。
<br />
<br />另外,调用者和被调用者之间是完全没有依赖关系的。
<br />
<br />
<br /><span style="font-size: large;"><span style="color: indigo;"><strong>Scale To RemoteActor</strong></span></span>
<br /><img src="http://dl.iteye.com/upload/attachment/216255/73e1ad32-74d2-3a7f-bdf6-bf9ff81f5efd.png" />
<br />Actor 的扩展性表现在:由于没有依赖关系,代码可以容易的分离出来放到远程机器上。后台的序列化、传输、反序列化(上图中的三个圈圈)都由 RemoteActor 完成了,Remote Actor 和本地 Actor 的使用方法几乎一模一样。
<br />
<br />
<br /><span style="font-size: large;"><span style="color: indigo;"><strong>Helloworld,RemoteActor</strong></span></span>
<br />
<br />下面是一个简单的 Remote Actor,它收到任何消息后就 bilibili 一下退出:
<br /><pre name="code" class="java">
import actors.Actor
import actors.remote.RemoteActor
// object: 创建 singleton class
object SimpleRemoteActor {
// main: 程序入口点
// 其实在声明中用 args 挺不对的,形参 param 非实参 arg,不过大家都这么用了……
def main(args: Array[String]) {
Actor.actor { // 创建并启动一个 actor
// 当前 actor 监听的端口: 3000
RemoteActor.alive(3000)
// 在 3000 端口注册本 actor,取名为 bilibili。
// 第一个参数为 actor 的标识,它以单引号开头,是 Scala 中的 Symbol 量,
// Symbol 量和字符串相似,但 Symbol 相等是基于字符串比较的。
// self 指代当前 actor (注意此处不能用 this)
RemoteActor.register('bilibili, Actor.self)
// 收到消息后的响应
Actor.react {case msg =&gt;
// bilibili 一下
println("bilibili")
}
}
}
}</pre>
<br />
<br />编译并运行
<br /><pre name="code" class="console">
scalac SimpleRemoteActor.scala
scala SimpleRemoteActor</pre>
<br />程序在 react 处挂起并等待消息。
<br />
<br />再写个程序向 SimpleRemoteActor 发送消息:
<br /><pre name="code" class="java">
import actors.Actor
import actors.remote.Node
import actors.remote.RemoteActor
object CallSimpleRemoteActor {
def main(args: Array[String]) {
Actor.actor {
// 取得一个节点(ip:port 唯一标识一个节点)
// Node 是个 case class,所以不需要 new
val node = Node("127.0.0.1", 3000)
// 取得节点对应的 actor 代理对象
val remoteActor = RemoteActor.select(node, 'bilibili)
// 现在 remoteActor 就和普通的 actor 一样,可以向它发送消息了!
remoteActor ! "a message"
}
}
}</pre>
<br />新起一个控制台,编译运行,再切回刚才的控制台看 SimpleRemoteActor。
<br />嗯,它已经 bilibili 完了。
<br />
<br />【注1】异步调用和同步调用的写法区别只在与是用 "!" 还是 "!?" 运算符。
<br />
<br />之前的笔记:
<br /><a target="_blank" href="http://night-stalker.iteye.com/blog/434886">http://night-stalker.iteye.com/blog/434886</a>
用 RemoteActor 制作一个弱弱的分布式缓存 - 2
<span style="font-size: large;"><span style="color: indigo;"><strong>Cache Server</strong></span></span>
<br />
<br />首先,对缓存做一个偷懒版的定义:它是个内存中的哈希表。
<br />
<br />基本操作分为两类: post 类和 get 类 【注1】
<br />
<br /><img src="http://dl.iteye.com/upload/attachment/216734/a37e4510-7103-3bc0-8a89-d44b4c98214a.png" />
<br />post 类的操作之间是同步的,post 类操作和客户端之间可以选择异步(put, remove)或者同步(带 reply 的处理,putEnsure, removeEnsure)。
<br />
<br /><img src="http://dl.iteye.com/upload/attachment/216736/39a3f618-8937-3e4a-b6c2-1d673eed7880.png" />
<br />get 类的操作之间是异步的,get 类操作和客户端之间是同步的。
<br />
<br />实现很简单(scala):
<br /><pre name="code" class="java">package bilibili.server
import actors.Actor
import actors.Actor._
import actors.remote.Node
import actors.remote.RemoteActor._
import collection.mutable.HashMap
object CacheServer {
// 创建 initialize actor,收到初始化消息后创建 cache cluster
def main(args: Array[String]) {
// 在 3000 端口注册名叫 initialize 的 actor,用于初始设置
actor {
alive(3000)
register('initialize, self)
// 接收到一个整数二元组消息后
// 在 portStart 到 portEnd 端口范围内的每个端口新建监听的 actor
react {case (portStart: Int, portEnd: Int) =&gt;
// 注:方法后紧随 _ 是 scala 中的一个惯用法,作用是将方法转换成可传递的对象
(portStart to portEnd).foreach(createActorsAt _)
}
}
}
def createActorsAt(port: Int) {
val cache = new HashMap[String, Object]
// 写入 cache,对同一 cache 的写操作是同步的
// 注:actor 收到的消息会自动形成 channel 队列
actor {
alive(port)
register('post, self)
loop {
react {
// 添加操作,与客户端异步
case ('put, key: String, value: Object) =&gt;
cache.put(key, value)
// 删除操作,与客户端异步
case ('remove, key: String) =&gt;
cache.remove(key)
// 添加操作,操作完毕返回一个值,与客户端同步
case ('putEnsure, key: String, value: Object) =&gt;
val originalValue = cache.put(key, value)
reply(if (originalValue == None) 'added else 'replaced)
// 删除操作,操作完毕返回一个值,与客户端同步
case ('removeEnsure, key: String) =&gt;
val originalValue = cache.remove(key)
reply(if (originalValue == None) 'noop else 'removed)
}
}
}
// 获取 cache,对同一 cache 可以有多连接同时读取
actor {
alive(port)
register('get, self)
loop {
// 分派函数
// 注:同时存在的子 actor 数量等于并发连接数
react {case msg =&gt;
// spawn 一个子 actor 用于读取
val getter = Actor.actor {
react {case key: String =&gt;
reply(cache.getOrElse(key, null))
}
}
// forward 消息到子 actor 中
getter.forward(msg)
}
}
}
}
}</pre>
<br />
<br />【注1】未考虑问题:冗余,expire,内存碎片,清理死亡 actor ……
用 RemoteActor 制作一个弱弱的分布式缓存 - 3
在客户端的眼中,缓存服务器是以节点为单位的。客户端通过一定的算法,决定应该从哪个节点获取 / 写入缓存。
<br />
<br /><img src="http://dl.iteye.com/upload/attachment/216742/1622b988-e173-321b-b399-75f7f9cc89e3.png" />
<br />
<br /><span style="font-size: large;"><span style="color: indigo;"><strong>Node Table</strong></span></span>
<br />
<br />其作用是使客户端可以按照 key 查询节点,并且使得在列表中添加、去掉一些 server 的时候不需要对其它 server 的缓存内容进行大出血的手术。(Scaling 需求)
<br />
<br />NodeTable 不涉及到 actor,可用 java 实现。它有 3 个方法:
<br /><pre name="code" class="java">// 添加一台 cache server 上的所有节点
public void addMachine(String ip);
// 去掉一台 cache server 上的所有节点
public void removeMachine(String ip);
// 查询一个 key 应该映射到哪个节点,返回 new NodeTuple(ip, port)
public NodeTuple queryNode(int keyHash)</pre>
<br />
<br />实现的策略可以用 Memcached 用的 <a target="_blank" href="http://gihyo.jp/dev/feature/01/memcached/0004?page=3">Consistent Hashing</a>。
<br />
<br />剩下的问题就是算节点 (ip, port) 的哈希值了,这里用个简单的算法:
<br /><pre name="code" class="java">(ip.hashCode() * 131071) ^ (port * 524287)
// 524287 == pow(2, 19) - 1
// 131071 == pow(2, 17) - 1
// 它们都是梅森素数
// key 要乘以一个值是因为字符串的 hashCode 是 CRC32 算出来的,
// 一堆临近的 ip 的 hashCode 相差 1,很不够分散
// 另外添上网卡的 MAC 地址或者其它特征要更好一些。</pre>
<br />
<br />NodeTable 的实现如下(TreeMap.ceilingEntry 要求 java 版本 1.6+):
<br /><pre name="code" class="java">package bilibili.client;
import java.util.*;
// ip, port
class NodeTuple {
public String ip;
public int port;
NodeTuple(String ip, int port) {
this.ip = ip;
this.port = port;
}
}
// tree map for 二分查找
public class NodeTable extends TreeMap&lt;Integer, NodeTuple&gt; {
private List&lt;Integer&gt; ports;
public NodeTable(int startPort, int endPort) {
ports = new ArrayList&lt;Integer&gt;();
for (int i=startPort; i&lt;endPort; i++) {
ports.add(i);
}
}
// 节点的哈希函数
int nodeHash(String ip, int port) {
return (ip.hashCode() * 131071) ^ (port * 524287);
}
// 往表中添加一台机器上的所有节点
public void addMachine(String ip) {
for(int port : ports) {
put(nodeHash(ip, port), new NodeTuple(ip, port));
}
}
// 去掉一台机器上的所有节点
public void removeMachine(String ip) {
for(int port : ports) {
remove(nodeHash(ip, port));
}
}
// 查询一个 key 应该映射到哪个节点,返回 (ip, port)
public NodeTuple queryNode(int keyHash) {
Map.Entry&lt;Integer, NodeTuple&gt; entry = ceilingEntry(keyHash);
if (entry == null) {
entry = firstEntry();
}
if (entry == null) {
return null;
} else {
return entry.getValue();
}
}
}
</pre>
<br />
<br />源文件存为 utf-8 编码格式并编译:
<br /><pre name="code" class="console">javac NodeTable.java -encoding utf8 -d .</pre>
<br />
<br />
<br /><span style="font-size: large;"><span style="color: indigo;"><strong>Client</strong></span></span>
<br />
<br />client 的实现应该是自明的(scala):
<br /><pre name="code" class="java">package bilibili.client
import actors.Actor._
import actors.remote.Node
import actors.remote.RemoteActor._
object CacheClient {
var nodeTable: NodeTable = null
var timeout = 300
// 启动 actor,使得可以在远程配置 nodeTable
actor {
alive(3000)
register('config, self)
loop {
react {
case ('initialize, portStart: Int, portEnd: Int) =&gt;
nodeTable = new NodeTable(portStart, portEnd)
case ('setTimeout, t: Int) =&gt;
timeout = t
case ('addMachine, ip: String) =&gt;
nodeTable.addMachine(ip)
case ('removeMachine, ip: String) =&gt;
nodeTable.removeMachine(ip)
}
}
}
def get(key: String): Object = {
if (nodeTable == null) return null;
// 查询节点
val node = nodeTable.queryNode(key.hashCode)
if(node != null) {
val get = select(Node(node.ip, node.port), 'get)
// "!?" 方法发送消息后,会等待并返回响应(对应 actor 中的 reply)
// 参数 timeout 为超时设置,单位为 msec
get !? (timeout, key)
} else null
}
def put(key: String, value: String) {
if (nodeTable == null) return;
// 查询节点
val node = nodeTable.queryNode(key.hashCode)
if(node != null) {
val post = select(Node(node.ip, node.port), 'post)
post ! ('put, key, value)
}
}
}</pre>
<br />
<br />编译
<br /><pre name="code" class="console">scalac CacheClient.scala</pre>
<br />
<br />ps:编译时需要 classpath 中包含 NodeTable.class 所在的包的路径。
用 RemoteActor 制作一个弱弱的分布式缓存 - 4
最后一篇,用 Cache Manager 将它们组装在一起。
<br />
<br />Cache Manager 负责通过消息发送初始化和配置 client 和 server,并维护 client 和 server 的列表。
<br />
<br /><img src="http://dl.iteye.com/upload/attachment/216749/d4d412f6-f4fb-3ccb-a559-4ef6cbd23e83.png" />
<br />
<br />【未完,坐火车去了。。。】
Eclipse 开发环境 Notes
<span style="color: violet;"><strong>java</strong></span>
<br />安装 jdk 即可,不用安装 jre。
<br />
<br /><span style="color: violet;"><strong>eclipse</strong></span>
<br />JavaEE 版本
<br />
<br /><span style="color: violet;"><strong>subversion 插件 subclipse</strong></span>
<br /><a target="_blank" href="http://subclipse.tigris.org/update_1.6.x">http://subclipse.tigris.org/update_1.6.x</a>
<br />
<br /><span style="color: violet;"><strong>lombok</strong></span>
<br />关闭 eclipse,
<br /><pre name="code" class="console">java -jar lombokxxx.jar</pre>
<br />
<br /><span style="color: violet;"><strong>maven</strong></span>
<br />下载 maven,按照下载页中说明配置 MAVEN_OPT,M2,M2_OPTS
<br />eclipse 配置 class path variable, M2_REPO = .../.m2/repository
<br />
<br />在 eclipse 里面 svn checkout 以后,eclipse 会在 checkout 目录下添加一个 .project 文件,影响子目录里面的 project 的搜索。先删除之,再导入正确的 eclipse 的设置:
<br /><pre name="code" class="console">mvn eclipse:eclipse</pre>
<br />
<br /><span style="color: violet;"><strong>content assist</strong></span> 和 code template 设置:
<br />设置触发键:.acefhmorstuwy
<br />
<br /><img src="http://dl.iteye.com/upload/attachment/223334/1c3b811a-5949-3a5d-af3e-3a8f434cec77.png" />
<br />
Lombok
<span style="color: olive;"><strong>@Getter @Setter</strong></span>
<br /><pre name="code" class="java">import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
public class GetterSetterExample {
@Getter @Setter private int age = 10;
@Setter(AccessLevel.PROTECTED) private String name;
}
</pre>
<br />
<br /><span style="color: olive;"><strong>@ToString</strong></span>
<br /><pre name="code" class="java">import lombok.ToString;
@ToString(excludes="id")
public class ToStringExample {
private String name;
private String[] tags;
private int id;
}
</pre>
<br />
<br /><span style="color: olive;"><strong>@EqualsAndHashCode</strong></span>
<br /><pre name="code" class="java">import lombok.EqualsAndHashCode;
@EqualsAndHashCode(exclude={"id", "shape"})
...</pre>
<br />
<br /><span style="color: olive;"><strong>@Data</strong></span>
<br /><pre name="code" class="java">import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;
@Data public class DataExample {
private final String name;
@Setter(AccessLevel.PACKAGE) private int age;
private double score;
private String[] tags;
@ToString(includeFieldNames=true)
@Data(staticConstructor="of")
public static class Exercise&lt;T&gt; {
private final String name;
private final T value;
}
}
</pre>
<br />
<br /><span style="color: olive;"><strong>@Cleanup</strong></span> 自动关闭 io
<br /><pre name="code" class="java">import lombok.Cleanup;
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}</pre>
<br />
<br /><span style="color: olive;"><strong>@Synchronized</strong></span>
<br /><pre name="code" class="java">import lombok.Synchronized;
public class SynchronizedExample {
private final Object readLock = new Object();
@Synchronized
public static void hello() {
System.out.println("world");
}
@Synchronized
public int answerToLife() {
return 42;
}
@Synchronized("readLock")
public void foo() {
System.out.println("bar");
}
}</pre>
<br />
<br />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment