七周七语言之Ruby笔记

简史

Ruby 出身于所谓的脚本语言家族,是一种解释型、面向对象、动态类型的语言 。解释型,意味着Ruby代码由解释器而非编译器执行。动态类型,意味着类型在运行时而非编译时绑定。

面向对象,意味着Ruby支持封装(把数据和行为一起打包)、类继承(用一棵类树来组织对象类型)、多态(对象可表现为多种形式)等特性。

尽管从执行速度上说,Ruby谈不上有多高效,但它却能让程序员的编程效率大幅提高。

作者谈到他喜欢Ruby寓编程于乐的方式。说到某个具体的技术点,最喜欢的是“代码块”(block)。代码块即是一种易于控制的高阶函数,也为DSL(Domain-Specific Language,领域特定语言)及其他特性的实现提供了极大的灵活性。

Ruby的编程模型

大部分的面向对象语言,都带有过程式余元要素,比如数字4在Java中就不是对象。另外某些函数式语言(如Scala)还加入了一些面向对象思想。

也有很多其他编程模型。基于栈的语言(如PostScript或Forth),使用一个或多个栈作为该语言的核心特征。基于逻辑的语言(如Prolog),是以规则(rule)为中心建立起来的。原型语言(如Io、Lua和Self)用对象而不用类来作为定义对象甚至继承的基础。

而Ruby是一门纯面向对象语言,一切皆对象。

鸭子类型

鸭子类型并不在乎其内在类型可能是什么。只要它像鸭子一样走路,像鸭子一样嘎嘎叫,那它就是只鸭子。

散列表

符号(symbol)是前面带有冒号的标识符,类似于:symbol的形式,可用作散列表的key。它在给事物命名时非常好用。尽管两个同值字符串在物理上不同,但相同的符号却是统一物理对象。我们可通过多次获取相同的符号对象标识符来证实这一点,像下面这样:

>> 'string'.object_id
=> 3092010

>> 'string'.object_id
=> 3089690

>> :string.object_id
=> 69618 

>> :string.object_id
=> 69618 

数组元素、散列表键、散列表值几乎可以任选类型,那么我们就能用Ruby构造出极为精妙的数据结构。

代码块和yield

代码块是没有名字的函数。它可以作为参数传递给函数或方法。

Ruby的一般惯例是:代码块只占一行时用大括号,代码块占多行时用do/end。

看看以下自定义实现的times方法:

class Fixnum 
  def my_times
    i = self
    while i > 0
       i = i - 1
       yield
   end
 end

代码块还可用作一等参数(first-class parameter),看看下面的例子:

def call_block(&block)
  block.call
end

def pass_block(&block)
  call_block(&block) 
end

pass_block {puts 'Hello, block'}

这技术能让你把可执行代码派发给其他方法。

定义类

所有的事物,归根结底都继承自Object。一个Class同时也是一个Module。

Class的实例将作为对象的模板。Fixnum是Class的一个实例,而4又是Fixnum的一个实例。每一个类同时也是一个对象。

Fixnum派生自Class类。从这里开始,你可能会有些费解。Class继承自Module,Module又继承自Object,说到底,Ruby中的一切事物都有一个共同祖先——Object。

类应以大写字母开头,并且一般采用骆驼命名法,如CamelCase。实例变量(一个对象有一个值)前必须加上@,而类变量(一个类有一个值)前必须加上@@。

attr关键字可用来定义实例变量。它有几种版本,其中最常用的版本是attr和attr_accessor。attr定义实例变量和访问变量的同名方法,而attr_accessor定义实例变量、访问方法和设置方法。

编写Mixin

过往经验表明,多继承不仅复杂,且问题多多。Java采用接口解决这一问题,而Ruby采用的是模块。模块是函数和常量的集合。如果在类中包含了一个模块,那么该模块的行为和常量也会成为类的一部分。

模块与包含它的类之间,是以一种相当隐秘的方式相互作用的。模块依赖的类方法通常不多。在Java中,这种依赖关系是显式的,即类会实现一个约束方法名的接口;而在Ruby中,这依赖关系是隐式的,即通过鸭子类型来实现。

学会利用简明的单继承,先定义类的主要部分,然后用模块添加额外功能。

这种由Flavors引入,在上至Smalltalk,下至Python的众多语言中采用的编程风格,就称作mixin。在这些语言中,带mixin的载体虽未必称作模块,但基本前提是一致的:使用单一继承结合mixin的方式,尽可能合理地把各种行为打包到一起。

模块、可枚举和集合

Ruby有两个至关重要的mixin:枚举(enumerable)和比较(comparable)。如果想让类可枚举,必须实现each方法;如果想让类可比较,必须实现<=>操作符。

collect和map方法把函数应用到每个元素上,并返回结果数组。find方法找到一个符合条件的元素,而select和find_all方法均返回所有符合条件的元素。你还可以用inject方法计算列表的和与积。

inject方法看似复杂,实则不然。它后面跟一个代码块,里面有两个参数和一个表达式。inject会通过第二个参数,把每个列表元素传入代码块,这样代码块就能在每个列表项上执行操作。第一个参数是代码块上一次执行的结果。

重大特征

元编程,说白了就是“写能写程序的程序”。Rails核心的ActiveRecord框架,就用元编程实现了一门简便易用的语言,以便编写连接数据库表的类。如果给department(部门)写个ActiveRecord类,写出来可能像下面这样:

class Department < ActiveRecord::Base
    has_many :employees
    has_one :manager
end

1.打开类

你和“开放类”曾有过一面之交,它可以随时改变任何类的定义,常用于给类添加行为。

利用这种重定义,我们甚至能让Ruby完全瘫痪,比如重定义Class.new方法。

对于开放类来说,这里的权衡主要考虑了自由。有这种随时重定义任何类或对象的自由,我们就能写出极为通俗易懂的代码。不过你也要明白,自由越大、能力越强,担负的责任也越重。

2.使用method_missing

Ruby找不到某个方法时,会调用一个特殊的调试方法显示诊断信息。该特性不仅让Ruby更易于调试,有时还能实现一些不易想到的有趣行为。只需要覆盖method_missing方法,我们就可以实现这些行为。

3.模块

说到Ruby最流行的元编程方式,非模块莫属。尽在模块中写上寥寥数行代码,就可以实现 def 或 attr_accessor 关键字的功能。其中一种技术是设计自己的DSL(domain-specific language,领域特定语言),再用DSL定义自己的类。该DSL在模块中定义各种方法,这些方法添加了对类进行管理所必需的全部方法和常量。可以通过宏(macro)的模块方法添加类行为。宏经常根据环境变化改变类行为。

只要某个模块被另一模块包含,Ruby就会调用该模块的included方法。记住,类也是模块。

所有这些元编程技术的有趣之处在于,程序可以根据它应用时的状态而改变。ActiveRecord利用元编程,动态添加与数据库中的列有相同名称的访问器。有些XML框架如builder,可允许用户通过method_missing方法定义自定义标签,以提供更加美观的语法。当代码的语法变得更美观,阅读代码的读者也就不必在语法问题上大伤脑筋,从而能更好地理解代码本身表达的意图。这正是Ruby的威力所在。

总结

一些最出色的Ruby框架,如Builder和ActiveRecord,都会为了改善可读性而特别依赖元编程。通过开放类技术编写的鸭子类型接口(为String对象和nil提供了blank?方法),会大大减少了用其他语言完成类似任务可能出现的大量杂乱无章的代码。另外利用method_missing方法可以写出漂亮的罗马数字代码。

不足

** 性能:** 1.9版甚至比过去快了10倍之多。Evan Phoenix写的新虚拟机Rubinius初步实现了用即时编译器(just-in-time compiler)编译Ruby代码。用这种方法观察大段代码在企业中应用的模式,以判断哪些代码可能多次使用。这对于Ruby的运行方式来说相当合适,但是Matz也十分清楚,他写Ruby是为了改善程序员的体验,而不是优化语言的性能。Ruby正是凭借它的许多特性(比如开放类、鸭子类型、method_missing等),击败了可编译并由此提升性能的语言。

** 并发问题:** 面向对象编程有一个重大弱点:该编程模型成立的一切前提条件,都建立在一种思想(围绕状态包装一系列行为)的基础之上,但通常,状态是会发生改变的。于是,程序中存在并发时,这种编程策略就会引发严重问题。最好的情况下,Ruby会产生大量的资源竞争;最坏的情况下,面向对象语言几乎无法在并发环境下调试程序,也无法可靠地测试程序。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容