Archive for June, 2013

为Sublime Text 2配置Ruby环境

“工欲善其事,必先利其器。”

尤其对于像我这样的工具控来说,Ruby还没好好学,代码也没好好写,就想得先把写代码的环境给搞好。选了Sublime Text 2做为文本编辑工具(虽说常弹出个是否购买的提示,但心里还是觉得宁静些),然后开始找些插件,比如Alignment, Markdown Editing等。当然目标还是要搞定Ruby的环境。

一开始其实没搞明白怎么运行,先看到了插件SublimeREPL,里面可以运行Ruby解释环境。就先开始折腾SublimeREPL,安装挺容易,但运行Ruby,pry出错,


/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:777:in `report_activate_error': Could not find RubyGem pry (>= 0) (Gem::LoadError)
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:211:in `activate'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems.rb:1056:in `gem'
from /Users/yangbin/Library/Application Support/Sublime Text 2/Packages/SublimeREPL/config/Ruby/pry_repl.rb:6

IRB(deprecated)可以运行,不过版本是1.8,OS系统提供的,而非我的RVM中在用的1.9.3的版本。

pry的问题折腾了我好久,直到发现了“Ruby REPL doesn’t work”,我在pry_repl.rb中加入了测试代码:


puts "+++++++++++++++++++++++"
puts "PATH:#{ENV['PATH']}"
puts "GEM_PATH:#{ENV['GEM_PATH']}"
puts "+++++++++++++++++++++++"

发现输出的结果不对。

再调整配置文件~/Library/Application Support/Sublime Text 2/Packages/User/SublimeREPL.sublime-settings为:


{
    // Add to PATH /usr/local/bin by default on OSX. 
    "default_extend_env": {
        "PATH": "/usr/local/bin:/Users/yangbin/.rvm/rubies/ruby-1.9.3-p125/bin/:{PATH}",
        "GEM_PATH": "/Users/yangbin/.rvm/gems/ruby-1.9.3-p125:/Users/yangbin/.rvm/gems/ruby-1.9.3-p125@global"
    }
}

之前犯了两个小错误,一是拷贝原始的文本时没注意“GEM_PATH”没有放在”default_extend_env”中,二是没有在用户的配置文件中设置。

正常后运行的结果如下:


+++++++++++++++++++++++
PATH:/usr/local/bin:/Users/yangbin/.rvm/rubies/ruby-1.9.3-p125/bin/:/usr/bin:/bin:/usr/sbin:/sbin
GEM_PATH:/Users/yangbin/.rvm/gems/ruby-1.9.3-p125:/Users/yangbin/.rvm/gems/ruby-1.9.3-p125@global
+++++++++++++++++++++++
[1] pry(main)> 

而要在Sublime Text 2中直接运行Ruby程序,从菜单选择“Tools – Build”(Command + B)即可。这里也涉及Ruby环境的设置,如果要使用RVM,修改~/Library/Application Support/Sublime Text 2/Packages/Ruby/Ruby.sublime-build文件:


{
    "cmd": ["/Users/yangbin/.rvm/bin/rvm-auto-ruby", "$file"],
    "file_regex": "^(...*?):([0-9]*):?([0-9]*)",
    "selector": "source.ruby"
}

参见Make rvm and Sublime Text 2 play nice

, ,

3 Comments

modules

模块(modules)用于组合方法、类和常量,主要有两个好处:

  1. 模块提供一个命名空间,避免名字冲突。
  2. 模块实现mixin

命名空间

命名空间(namespaces)没啥,用于防止载入两个不同的文件(使用require),有命名冲突的问题,这样可以使用module.XXX 避免。

mixins

之前说Ruby没有多重继承,而可以mixin来替代,没搞懂。今天细看了一下文档。

模块没有实例,它不是类。然而你可以在类定义中引入模块(使用include),此时所有模块的实例方法就在类中有效,这个机制被称为mixed in,被混入的模块就像是一个父类。

首先,Ruby对文件什么也没做。在c中使用#include 头文件,预处理在编译时将头文件内容插入源代码文件中。而Ruby的include只是设置了一个到模块的引用,如果该模块在不同的文件必须先require。

其次,Ruby include并没有简单地复制模块的实例方法到类中,相反,它只是设置了一个从类到引入的模块的引用。如果多个类引入了模块,它们的指向是相同的。假如你改变了模块中方法的定义,即使在程序运行期间,所有引入模块的类都会展示相同的行为。

Ruby Comparable就是一个标准的mixin,可以在一个类中引入Comparable,只需要类定义了<=>方法,那么引入Comparable的类就有了比较运算符(<, <=, ==, >=, >)和between? 方法。比如:


class Song
  include Comparable
  def (other)
    self.duration  other.duration
  end
end

其他的模块还有:Enumerable, Errno, FileTest, GC, Kernel, Marshal, Math, ObjectSpace, Process。

在一个mixin中如果有实例变量会怎么样?记住在Ruby**中“第一个引用了@变量的会在当前对象self中创建该实例变量。”**所以对于mixin来说,会在混入的client类中创建实例变量。

大多数情况下,mixin模块不要保存自己的实例数据,而使用accessors去从客户对象获取数据。如果真的需要在mixin中保存状态,确保实例变量在不同的mixin有唯一的名字(比如说使用模块名作为前缀)。

Ruby有两种方法在你的代码中引入其他文件:

  • load “filename.rb”
  • require “filename”

load是一个方法,每次执行时都会引入Ruby的源文件;而require只会载入一次,require还能记载共享的二进制库。两个都支持相对路径和绝对路径,如果指定相对路径,则从当前的装载路径($:)搜索每个目录。

, ,

1 Comment

throw:catch和raise:rescue的不同

Exceptions, Catch, and Throw前半部分的时候听清楚的,Exceptions大家也都差不多,不就是定义异常,抛出异常,捕捉异常嘛,只不过ruby没有用try/catch/throw,而搞了套新名词,begin/rescue/else/ensure/end, raise罢了。

可看到后半部分,又出现了catch/throw,就有点糊涂了。
“While the exception mechanism of raise and rescue is great for abandoning execution when things go wrong, it’s sometimes nice to be able to jump out of some deeply nested construct during normal processing. This is where catch and throw come in handy.”

那例子也觉得绕。既然我都有了rescue/raise,还要catch/throw干嘛呢?

网上搜了一下,stackoverflowrubylearning分别有两篇好文章。一个是What is catch and throw used for in Ruby?,一个是Throw, Catch, Raise, Rescue… I’m so confused!

简单地说,rescue/raise是用来应对异常的,不过有的时候我们处理的不是异常,又需要简明优雅的方式能跳出一堆的处理流程(比如说退出多层嵌套的循环,往往我们要break),这时我们就可以使用catch/throw,有点嘎然而止的意味。

比如stackoverflow上的例子:


INFINITY = 1.0 / 0.0
catch (:done) do
  1.upto(INFINITY) do |i|
    1.upto(INFINITY) do |j|
      if some_condition
        throw :done
      end
    end
  end
end

还有两篇文章都提到的实例sintra中在使用的halt,就是在halt方法中throw :halt,然后在invoke中catch该:halt信息,你可以在任何地方使用halt中止一个请求。

还有一个自己的理解,可以使用rescue/raise来实现,但rescue/raise通常是用来处理异常,需要堆栈调用信息,更重一些,效率上应该也有影响。

sintra后面再找时间学习一下。

, ,

1 Comment

Ruby Exception

Ruby Exception的层次结构,Exception类的下一层有fatal, NoMemoryError, ScriptError, SecurityError, SignalException, StandardError, SystemExit和SystemStackError。

大多数异常继承自StandardError,这部分是一般Ruby程序需要处理的,其他偏底层和更严重。每个Exception都关联一个消息串和堆栈的调用层次。

通常使用异常的形式(else并不常用),在异常处理中可以使用retry,重复执行begin/end块。


f = File.open("testfile")
begin
  \# .. process
rescue
  \# .. handle error1
rescue
  \# .. handle error2
else
  puts "No errors."
ensure
  f.close unless f.nil?
end

使用raise(Kernal::raise方法)来触发异常,典型的形式有三种:


raise
raise "bad mp3 encoding"
raise InterfaceException, "Keyboard failure", caller
  • 第一个最简单,就是重新触发当前的异常。如果当前无异常,就触发RuntimeError。
  • 第二个触发RuntimeError,并提供错误消息。
  • 第三个指定异常类和消息,最后还有个参数堆栈跟踪,一半是Kernel::caller方法,该方法返回当前的执行堆栈信息,一个数组包含”文件:行号 在方法”。

, ,

1 Comment

第三周的总结

坚持100天学习Ruby,第三周,小结一下。

  • 继续看Programming Ruby – The Pragmatic Programmer’s Guide两章,”More about methods”和”Expresssions”,还有”Standard Types”上次剩余的部分,看来也就是两章左右的速度。
  • 时间还是在中午休息时间,反而周末不固定,时间没能安排好。写代码没有,是个问题。
  • 这部分内容比较常规,Ruby比较特殊的点是:
    >- 方法都是有返回值的。
    >- 一些特殊的符号用在方面后面,?,!
    >- 特殊的全局变量
    >- 表达式范围也很广,都是表达式。
    >- 特殊的条件运算符,以及和对应的方法匹配,比如===和case/when,<=>和range。

现在感觉有点瓶颈,状态不是很好,得反思一下。

, ,

Leave a comment

Ruby中的条件、循环控制表达式

条件

  • 非(nil或者false)的都是true,注意数值0也是true。
  • defined? 用于返回参数的描述,如果参数未定义则返回nil。
  • 几个特殊的比较运算符:===用于case when子句测试是否相等, <=>之前说了,返回-1,0,+1,可以用于自定义range中, =~正则表达式匹配, eql?判断接收者和参数类型和值都相同, equal?判断接收者和参数有相同的对象ID。注意==和=~对应的有!=和!~。
  • 可以使用range作为条件表达式。之前都是false,直到第一个条件为真,然后一直都是true,直到第二个条件为true。然后range重置。比如这个例子:
    
    file = File.open("ordinal")
    while file.gets
      print  if /third/ .. /fifth/
    end
    
  • 除了if还有unless。
  • case子句,参见下例。case后的表达式结果和when后的表达式通过===进行比较,需要支持===(内置类都支持的),比如正则表达式的===就是模式匹配。
    
    kind = case year
             when 1850..1889 then "Blues"
             when 1890..1909 then "Ragtime"
             when 1910..1929 then "New Orleans Jazz"
             when 1930..1939 then "Swing"
             when 1940..1950 then "Bebop"
             else                 "Jazz"
           end
    

循环

  • 除了while,还有until。
  • 和if, unless一样,while/until可以做语句的修饰符,比如:a *= 2 while a < 100。
  • ruby提供的一堆循环的形式,times, downto, unto, step:
    >- 3.time
    >- 0.upto(9)
    >- 0.step(12, 3)
    >- array.each
    >- loop
    >- for .. in
  • for和each的区别,Ruby会把for翻译成each的形式,并在范围外定义一个变量,该变量在循环结束后还可以使用。
    
    for aSong in songList
      aSong.play
    end
    # Ruby translates it into something like:
    songList.each do |aSong|
      aSong.play
    end
    
  • 只要类定义了each方法,就可以使用for循环。
  • break/redo/next/retry,break跳出循环,redo重新来一次循环,next继续下一次循环,retry更恨,全部重新来过。

, ,

1 Comment

野蛮的侵略者(三)

之前我们说了盎格鲁和撒克逊人汪达尔人和法兰克人。意大利北部还有哥特人。但真正厉害的是在遥远的东北部地区有一个部落,罗马人和日耳曼部落的人都觉得那里的人凶猛无比,十分可怕。

“中国人也觉得他们很厉害。”我说。

“那是谁呀?”

“他们就是匈奴,汉朝的时候和匈奴是不是经常打战的,卫青、霍去病。”

“是的,还有丝绸之路。”

“不过不知道我们所说的匈奴人和欧洲的匈奴人是否是一样的?”(注:据维基百科,入侵东、西罗马帝国的称为匈人,也有说匈奴人,但他们和中国古代的匈奴是否有血缘关系或系同一民族尚无定论。)

尽管日耳曼人本身都是勇猛的战士,但他们还是很怕匈奴人,和罗马人打战相比就要轻松多了。有一个叫阿提拉的匈奴首领吹嘘说,凡是他的马蹄踏过之处,寸草不生。阿提拉率领匈奴人从遥远的东方不断扩张,差不多就打到了巴黎,最后罗马和日耳曼人联合抵抗匈奴人的进攻,在离巴黎不远的一个叫沙隆的地方展开了激烈的战斗,这场战争就是历史上十分著名的“沙隆之战”。

日耳曼人拼死一战,他们杀红了眼,这一战尸横遍野、血流成河。

“中国的打战也经常用这些词的。”

匈奴人终于被打败了,幸亏他们被打败了,如果得胜的话,这些疯狂的莪也骂人可能会征服全世界呢。所以公元451年的沙隆战役在历史上十分重要。

“不然后果不堪设想。”

 

阿提拉和匈奴人在沙隆被击败之后,又想去打罗马人。他们攻向南边的意大利,一直到了罗马。

这时罗马的教皇名叫利奥一世,利奥(Leo)是狮子(Lion)的意思,不过利奥一世可不象狮子那样勇猛,他本人既不是军人,也不是战士。但他和红衣主教团及其他一些主教却走出罗马,前去会见阿提拉,简直像羊入虎口,但不可思议的事情发生了。

“怎么了?”

具体情况没人知道,可能阿提拉被这些基督徒的气势和光芒震慑了,反正阿提拉没有伤害他们,也没有入侵罗马,而是打道回府,老老实实、永远地离开了意大利,回到了北边不为人知的老家。是不是比诸葛亮的空城计还厉害。

 

不过可怕的阿提拉刚走,非洲的汪达尔人觉得有机可乘,就从非洲度过台伯河到了罗马,不费吹灰之力就攻下了罗马,并洗劫一空。

可怜的古罗马!“永恒之城”终于被打败,而且是彻底被打垮了。罗马城的失陷是在公元476年。从此,以罗马为都城的帝国西部地区四分五裂,分别为日耳曼部落中各个不同的部族所统治。只有以君士坦丁堡为都城的东部地区还继续存在,保持了将近一千年。

 

人们把476年作为古代历史的结束,(我们中国可不是按这个来划分古代历史的。)像这样的日期既准确又方便记忆。

“你知道怎么方便记忆吗?”

“不知道。”

“476,就是逝去了。古罗马逝去了。”

 

欢迎订阅讲述我和小乐成长中故事的微信账号: fuyunv  .

, , ,

Leave a comment

野蛮的侵略者(二)

昨天说了日耳曼人金发碧眼,和地中海周围的人黑发黑眼不同。尽管有些日耳曼人已经迁移到罗马帝国,但多数都生活在人烟稀少的地区,住在木头小屋。女人们种菜、饲养牛马,男人们打猎、作战和打铁。打铁是非常重要的,因为铁匠可以制成用来作战的剑和长矛,还有各种生产工具。这就是为什么史密斯这个名字在他们中间非常受尊敬的原因。

“史密斯(Smith)就是铁匠的意思。”

大约在公元400年时,这些北方的邻居成了最让罗马人头痛的麻烦。先是流窜到罗马的北部地区,后来就赶不走了,再后来日耳曼部落中有两支进入了不列颠地区,住在那里的罗马人发现大势已去,只有回到罗马,把土地和原住民都让给了这些侵略者。

“你知道是哪两支吗?”

“嗯,是维京人吗?”

“不是。”

在不列颠安顿下来的两支部落分别是盎格鲁人和撒克逊人。

“哦,对了。”(在好玩的英国历史中说到过,盎格鲁人、撒克逊人、维京人、诺曼人。)

因此这个地方后来叫做盎格鲁人的土地,简略地讲,就是“盎格兰”,而盎格兰这个词经过很多年的变音后,就变成了我们今天所说的“英格兰”——英国。

“盎格兰,England, [‘iŋɡlænd], land就是陆地的意思。”

“老爸,你念错了,应该是[‘iŋɡlənd]。”

“啊,我一直都是这么念的,我看看。”

“还真是啊,land念lænd,但是England念[‘iŋɡlənd]。好吧,我错了。”

另一支叫汪达尔的部落进入了高卢地区,高卢就是现在的法国。接着他们继续南下到西班牙,在那里烧杀抢掠。后来有到北非,一路破坏,所以我们现在把有人恶意地毁坏财务的人称为“汪达尔人”。

在汪达尔部落之后,有一支叫法兰克的部落进入了高卢,不过他和汪达尔人不一样,他在这里定居,并把那个国家顶名为法国。

欢迎订阅讲述我和小乐成长中故事的微信账号: fuyunv  .

, , , , ,

1 Comment

野蛮的侵略者(一)

罗马城和罗马帝国的日子终于也走到了头,这个帝国的强盛已经达到了巅峰,也该到它走向衰落的时候了。终于轮到罗马被别的国家所征服,可是你肯定猜不到是哪里的人们征服了罗马,并成了下一个世界霸主。

希利尔讲故事的时候,经常就带着问题,也很符合我讲故事的风格,边讲边问,让你自然而然地参与到故事中去。我很喜欢这种“随文发问”的方式,使得讲故事和听故事的人不是孤立的。

几千年来,日耳曼部落一直生活在罗马帝国的北部边界。他们时不时地要骚扰一下罗马,罗马人将这些人称为野蛮人,其实他们把除了罗马人之外的所有人都叫做野蛮人,认为野蛮人凶猛好斗。

多数日耳曼人是蓝色眼睛,浅色头发,也就是我们所说的金发碧眼,希腊人和罗马人以及其他生活在地中海周围的人都是深色头发,黑色眼睛,我们现在叫黑发黑眼。

这些北方的民族信奉的一些神灵和希腊人、罗马人不同,你也许能猜到,他们的主神是战神,叫沃登。沃登同时也是天神,他就象是希腊两个神——天神宙斯和战神阿瑞斯的结合体。据说,沃登住在天上一个叫瓦尔哈拉的美丽宫殿里,有很多童话。

 

“可惜我们都不知道。不过我们的星期里有一天是以沃登的名字命名的。小乐,你知道是哪一天吗?”

小乐默念各个星期几的英文,然后告诉我说:“星期三。”

对了,星期三(Wednesday)曾经写作Wodensday,就是以沃登(Woden)的名字命名的。

沃登是日耳曼的主神,还有托尔,是雷电之神。

“你知道他又是来命名哪一天的呢?”

“Tuesday”

“不对,是Thursday,星期四。”

星期四以前是Thorsday,就是以托尔(Thor)的名字命名的。

 

还有蒂乌(Tiu),命名了星期二(Tuesday)。弗雷亚(Freya)。

“那就是Friday,星期五。”

因此,我们的一周有四天都是以日耳曼神灵的名字命名的。

 

“现在剩下的还有哪几天呀?”

“星期天Sunday,星期六Saturday,星期一Monday。”

“你看看Sunday和Monday是以什么来命名的?”

“Sunday是太阳(Sun)。”小乐先发现了一个,“嗯,还有Monday,Mon是mom,妈妈吗?”

“不是,是月亮。”

“月亮是moon。”

“嗯,moon,mon差了一个o。好了,现在我们只剩最后一天了。”

星期六(Saturday)是源于一位罗马神灵萨杜恩(Saturn)的名字。

“Saturn也是土星,乐乐,你还记得我们以前说过太阳系的行星是以罗马的神来命名的。一个星期里,四个日耳曼神,一个罗马神,还有两个是太阳和月亮。好玩吧?”

“好玩。”

 

欢迎订阅讲述我和小乐成长中故事的微信账号: fuyunv  .

, , , , ,

1 Comment

第一次世界大战

前两天小乐选择了听德国的故事,结果说了第二次世界打战的两个章节,不过没整理。今天则反过头来说第一次世界大战。

欧洲有个小国叫塞尔维亚,是大国奥地利的邻国,不过两个国家的关系不好。

“这里得先说明一下大国奥地利可不是现在的奥地利,而是奥匈帝国,你明天可以从地图上去看一看,包括了现在的奥地利、匈牙利、波兰、捷克斯洛伐克很多国家。”

奥地利除了管理奥地利人外,还统治其他一些种族的人,有的和塞尔维亚人血统相近。塞尔维亚人总是说奥地利人对这些人非常不公平,就经常送人去奥地利闹事,奥地利说塞尔维亚煽动人们法对奥地利的统治,想要分裂。

后来就有一个住在塞尔维亚的年轻人枪杀了奥地利王子,这个王子是奥地利下一任国王的候选人。奥地利就和塞尔维亚开战了。

乱子慢慢扩散开来,就像离离原上草一样,火越烧越旺。

“他也知道这个诗句的吗?”

“哦,那不知道的,是老爸加的。”

“啊,真讨厌!”

俄国站在塞尔维亚这边,德国则支持奥地利。自从普法战争后,欧洲大国都在为战争训练士兵。几乎所有的欧洲国家都分别归入两个阵营,一个是德国的友邦,一个是法国的朋友。

“普鲁士就是后来的德国。”

俄国是法国的朋友,当俄国准备出战后,法国也命令自己的军队随时准备志愿俄国,那德国就处于两个大敌之间。

“法国在德国的西部,俄国在德国的东部。”(了解历史,地理确实是必不可少啊。)

德国就像在俄国进行之前,出其不意地干掉法国。要尽快到达法国,德国必须经过小国比利时,这样德国就直接把要阻拦他们的比利时推到了一边,冲向巴黎,他们最远到达了马恩河,这里离巴黎只有30多公里,被法军给拦住了。马恩河战役可能是你迄今为止听说过的最著名的战役之一。

“我们倒是都没听说过。”

后来英国加入了战争,站在了法国、比利时和俄国这边。

俄国自己突然爆发了革命,杀死了他们的统治者——沙皇和他的一家,退出了战争。

美国在1917年,也就是战争开始后的第三年加入了战争。

“你知道第一次世界大战是哪一年开始的吗?”

“1914年。”

“再想想,1917年是第三年。”

“哦,我想想…,是1915年。”

自从画了时间楼梯后,我经常有意识地会小乐强调一下时间的发生时间,并在次日会问问小乐是什么时间呀?

最后,德国和它的盟国在1918年11月11日投降。并签订了一个文件,同意同盟国的所有要求。历史上第一次世界大战就此落幕。德国成为了共和国,大奥地利变成了小奥地利,因为它原有的那些土地和人民都分裂了,成了一个个独立的小国。小塞尔维亚完全消失了,在它的位置上建立了新的国家——南斯拉夫,其中包括塞尔维亚和其他一些小邦国。

“希利尔没有想到的是,后来南斯拉夫又消失了,又重新变成了塞尔维亚和一些小邦国。”

“啊!”

“因为这些地方也都是多民族的国家,矛盾会比较多,分分合合。你想,我们国家历史朝代,唐、宋早期很多周边地域也都不属于国家中的,元朝倒是很大,俄罗斯都包括进来了,但后来也都分裂了。”

“象蒙古就独立出去了。”

“是的。”

故事讲完了,晚安。

题图:第一次世界大战前后的欧洲地图,可以对比一下。

欢迎订阅讲述我和小乐成长中故事的微信账号: fuyunv  .

, , , ,

Leave a comment