Ruby 魔法 学习笔记之一

一、向对象显示的发送消息
我们可以向对象直接发送消息:
Ruby代码


代码如下:

class HelloWorld
def say(name)
print "Hello, ", name
end
end
hw = HelloWorld.new
hw.send(:say,"world")

我们通常使用hw.say("world"),但send可以对private的方法起作用。
不光如此send可以使程序更加动态,下面我们看看一个例子:
我们定义了一个类Person,我们希望一个包含Person对象的数组能够按
照Person的任意成员数据来排序:
Ruby代码


代码如下:

class Person
attr_reader :name,:age,:height
def initialize(name,age,height)
@name,@age,@height = name,age,height
end
def inspect
"#@name #@age #@height"
end
end

在ruby中任何一个类都可以随时打开的,这样可以写出像2.days_ago这样优美
的code,我们打开Array,并定义一个sort_by方法:
Ruby代码
class Array
def sort_by(sysm)
self.sort{|x,y| x.send(sym) <=> y.send(sym)}
end
end
我们看看运行结果:
Ruby代码
people = []
people << Person.new("Hansel",35,69)
people << Person.new("Gretel",32,64)
people << Person.new("Ted",36,68)
people << Person.new("Alice", 33, 63)
p1 = people.sort_by(:name)
p2 = people.sort_by(:age)
p3 = people.sort_by(:height)
p p1 # [Alice 33 63, Gretel 32 64, Hansel 35 69, Ted 36 68]
p p2 # [Gretel 32 64, Alice 33 63, Hansel 35 69, Ted 36 68]
p p3 # [Alice 33 63, Gretel 32 64, Ted 36 68, Hansel 35 69]
这个结果是如何得到的呢?
其实除了send外还有一个地方应该注意attr_reader,attr_reader相当于定义了name,
age,heigh三个方法,而Array里的sort方法只需要提供一个比较方法:
x.send(sym) <=> y.send(sym) 通过send得到person的属性值,然后在使用<=>比较
二、定制一个object
<< object
ruby不仅可以打开一个类,而且可以打开一个对象,给这个对象添加或定制功能,而不影响
其他对象:
Ruby代码
a = "hello"
b = "goodbye"
def b.upcase
gsub(/(.)(.)/)($1.upcase + $2)
end
puts a.upcase #HELLO
puts b.upcase #GoOdBye
我们发现b.upcase方法被定制成我们自己的了
如果想给一个对象添加或定制多个功能,我们不想多个def b.method1 def b.method2这么做
我们可以有更模块化的方式:
Ruby代码
b = "goodbye"
class << b
def upcase # create single method
gsub(/(.)(.)/) { $1.upcase + $2 }
end
def upcase!
gsub!(/(.)(.)/) { $1.upcase + $2 }
end
end
puts b.upcase # GoOdBye
puts b # goodbye
b.upcase!
puts b # GoOdBye
这个class被叫做singleton class,因为这个class是针对b这个对象的。
和设计模式singleton object类似,只会发生一次的东东我们叫singleton.
<< self 给你定义的class添加行为
Ruby代码
class TheClass
class << self
def hello
puts "hello!"
end
end
end
TheClass.hello #hello!
<<self修改了你定义class的class,这是个很有用的技术,他可以定义class级别
的helper方法,然后在这个class的其他的定义中使用。下面一个列子定义了访问
函数,我们希望访问的时候把成员数据都转化成string,我们可以通过这个技术来
定义一个Class-Level的方法accessor_string:
Ruby代码
class MyClass
class << self
def accessor_string(*names)
names.each do |name|
class_eval <<-EOF
def #{name}
@#{name}.to_s
end
EOF
end
end
end
def initialize
@a = [ 1, 2, 3 ]
@b = Time.now
end
accessor_string :a, :b
end
o = MyClass.new
puts o.a # 123
puts o.b # Fri Nov 21 09:50:51 +0800 2008
通过extend module给你的对象添加行为,module里面的方法变成了对象里面的
实例方法:
Ruby代码
module Quantifier
def any?
self.each { |x| return true if yield x }
false
end
def all?
self.each { |x| return false if not yield x }
true
end
end
list = [1, 2, 3, 4, 5]
list.extend(Quantifier)
flag1 = list.any? {|x| x > 5 } # false
flag2 = list.any? {|x| x >= 5 } # true
flag3 = list.all? {|x| x <= 10 } # true
flag4 = list.all? {|x| x % 2 == 0 } # false
三、创建一个可参数化的类:
如果我们要创建很多类,这些类只有类成员的初始值不同,我们很容易想起:
Ruby代码
class IntelligentLife # Wrong way to do this!
@@home_planet = nil
def IntelligentLife.home_planet
@@home_planet
end
def IntelligentLife.home_planet=(x)
@@home_planet = x
end
#...
end
class Terran < IntelligentLife
@@home_planet = "Earth"
#...
end
class Martian < IntelligentLife
@@home_planet = "Mars"
#...
end
这种方式是错误的,实际上Ruby中的类成员不仅在这个类中被所有对象共享,
实际上会被整个继承体系共享,所以我们调用Terran.home_planet,会输出
“Mars”,而我们期望的是Earth
一个可行的方法:
我们可以通过class_eval在运行时延迟求值来达到目标:
Ruby代码
class IntelligentLife
def IntelligentLife.home_planet
class_eval("@@home_planet")
end
def IntelligentLife.home_planet=(x)
class_eval("@@home_planet = #{x}")
end
#...
end
class Terran < IntelligentLife
@@home_planet = "Earth"
#...
end
class Martian < IntelligentLife
@@home_planet = "Mars"
#...
end
puts Terran.home_planet # Earth
puts Martian.home_planet # Mars
最好的方法:
我们不使用类变量,而是使用类实例变量:
Ruby代码
class IntelligentLife
class << self
attr_accessor :home_planet
end
#...
end
class Terran < IntelligentLife
self.home_planet = "Earth"
#...
end
class Martian < IntelligentLife
self.home_planet = "Mars"
#...
end
puts Terran.home_planet # Earth
puts Martian.home_planet # Mars
四、Ruby中的Continuations:
Continuations恐怕是Ruby中最难理解的概念了,它可以处理非局部的跳转,
它保存了返回地址和执行的环境,和c中的setjmp和longjump类似,但它保存
了更多的信息:
axgle举的曹操的例子很形象,我们拿过来看看:
来自[http://www.javaeye.com/topic/44271]
曹操(caocao)被誉为“古代轻功最好的人 ”,是因为“说曹操,曹操到”这句名言。
在ruby中,曹操的这种轻功被称为callcc.
Ruby代码
callcc{|caocao|
for say in ["曹操","诸葛亮","周瑜"]
caocao.call if say=="曹操"
puts say #不会输出,因为曹操已经飞出去了
end
}#“曹操”飞到这里来了(跳出了callcc的block,紧跟在这个block后面,继续执行下面的ruby代码)
puts "到"
callcc里的caocao是个"延续"(Continuation)对象.这个对象只有名叫“call"的这样一个方法。
当执行了caocao.call后,caocao就会飞到callcc的块(block)后面,让ruby继续执行其下面的代码。
我上面给出的是一个从块里头”飞“到块外面的例子;下面是Programming Ruby给出的从代码后面”飞“到代码前面的例子:
Ruby代码
arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]
callcc{|$cc|}#下面的$cc.call如果被执行,就会飞回到这里(callcc的块之后)。
puts(message = arr.shift)
$cc.call unless message =~ /Max/
例子大多来自<<The ruby way>>

(0)

相关推荐

  • ruby 学习笔记(1) 初识语法

    单从技术而言,ruby本身确实很爽,令程序员的工作变得轻松有趣! 下面的代码演示了如何找出100以内的素数: 复制代码 代码如下: using System; namespace Mersenne { class Program { static void Main(string[] args) { for (int i = 2; i < 50; i++) { if (CheckDigital(i)) { Console.WriteLine("{0} ",i); } } Cons

  • Ruby学习笔记之gem 命令详解

    ruby -v #查看ruby 版本 ruby -e ''require"watir"; puts Watir::IE::VERSION'' #查看watir版本 rvm list known #列出已知的ruby版本 rvm install 1.9.3 rvm use 1.9.3 | rvm use system rvm use 1.9.3 --default rvm list #列出本地ruby版本 rvm remove 1.9.2 具体详情见RVM实用指南  https://ru

  • Ruby学习笔记一

    笔记: Ruby会默认返回方法中的最后一个值. 如果需要返回多个值,可以用数组来实现. 可以通过强制声明return来返回值. 基础的输出方法是用puts(输出一行),print(直接打印). 基础的输入方法是gets(读入一行,包含行尾的'\n',可以用chomp方法消除). 纯粹的现象对象语言,而且还是一个动态语言(虽然今天目前还没用到),所以老老实实面向对象去. 有两种简单粗暴的储存多个元素的方法.数组[]和哈希表{},而且还有各种各样的函数. 强烈推荐新手入门在线学习网站: http:/

  • ruby 学习笔记(2) 类的基本使用

    ruby语言跟c#的一些重要差别在于: 1.ruby是动态语言,c#是静态语言--即对象在new出来以后,ruby还可以动态给对象实例添加一些属性或方法(javascript也是如此) 2.ruby中刻意弱化了变量类型这个概念,默认情况下变量/方法都不需要声明具体(返回)类型,但其实在ruby内部,会自动根据变量的值分配类型.(可以通过 "puts 变量.class"查看) 3.ruby相对c#来讲,可能有些雷的地方在于:父类中的private成员,居然是可以在子类中使用的! ...其

  • Ruby 魔法 学习笔记之一

    一.向对象显示的发送消息 我们可以向对象直接发送消息: Ruby代码 复制代码 代码如下: class HelloWorld def say(name) print "Hello, ", name end end hw = HelloWorld.new hw.send(:say,"world") 我们通常使用hw.say("world"),但send可以对private的方法起作用. 不光如此send可以使程序更加动态,下面我们看看一个例子: 我

  • Ruby简洁学习笔记(二):类继承、属性、类变量

    1.如何声明一个子类 复制代码 代码如下: class Treasure < Thing 这样Thing类中的属性name,description都被Treasure继承 2.以下三种方式传入父类initialize方法的参数分别是什么? 复制代码 代码如下: # This passes a, b, c to the superclass def initialize( a, b, c, d, e, f ) super( a, b, c ) end # This passes a, b, c t

  • Ruby简洁学习笔记(一):字符串、数字、类和对象

    为了证明Ruby真的好用,hello world也能写的如此简洁: 复制代码 代码如下: puts 'hello world' 1.输入/输出 复制代码 代码如下: print('Enter your name') name=gets() puts("Hello #{name}") 注:Ruby是区分大小写的 2.String类 puts("Hello #{name}")中的变量 name是内嵌在整个String里的,通过 #{ } 包裹进行内嵌求值,并用双引号&q

  • Javascript学习笔记之 对象篇(四) : for in 循环

    先上范例: // Poisoning Object.prototype Object.prototype.bar = 1; var foo = {moo: 2}; for(var i in foo) { console.log(i); // prints both bar and moo } 这里我们要注意两点,一是 for in 循环会忽略 enumerable 设置为 false 的属性.例如一个数组的 length 属性.第二是,由于 for in 会遍历整个原型链,所以当原型链过长时,会

  • angularjs学习笔记之简单介绍

    一.angularjs简介 AngularJS 是一个为动态WEB应用设计的结构框架.它能让你使用HTML作为模板语言,通过扩展HTML的语法,让你能更清楚.简洁地构建你的应用组件.它的创新点在于,利用 数据绑定 和 依赖注入,它使你不用再写大量的代码了.这些全都是通过浏览器端的Javascript实现,这也使得它能够完美地和任何服务器端技术结合. 说了这么多,估计你啥都没有理解...对吗?别着急,我来说说他的几个特点吧:模块化,数据双向绑定,依赖注入,指令.下面我们就跟着这几个特点进行学习.

  • AngularJS学习笔记之表单验证功能实例详解

    本文实例讲述了AngularJS学习笔记之表单验证功能.分享给大家供大家参考,具体如下: 一.执行基本的表单验证 <!DOCTYPE html> <html ng-app='exampleApp'> <head> <meta charset="UTF-8"> <title>表单</title> <script src="../../js/angular.min.js" type="

  • 正则表达式学习笔记

    正则表达式学习笔记 正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含  有某种子串.将匹配的子串做替换或者从某个串中取出符合某个条件的子串等. 列目录时, dir *.txt或ls *.txt中的*.txt就不是一个正则表达式,因为这里*与正则式的*  的含义是不同的. 为便于理解和记忆,先从一些概念入手,所有特殊字符或字符组合有一个总表在后面,最后一  些例子供理解相应的概念. 正则表达式 是由普通字符(例如字符 a 到 z)以及特殊字符(

  • vue mint-ui学习笔记之picker的使用

    本文介绍了vue mint-ui picker的使用,分享给大家,也给自己留个学习笔记 Picker的使用 import { Picker } from 'mint-ui'; Vue.component(Picker.name, Picker); API 示例一:picker的简单使用 xxx.vue: <template> <div id="app"> <mt-picker :slots="slots" ></mt-pic

  • Go语言学习笔记之反射用法详解

    本文实例讲述了Go学习笔记之反射用法.分享给大家供大家参考,具体如下: 一.类型(Type) 反射(reflect)让我们能在运行期探知对象的类型信息和内存结构,这从一定程度上弥(mi)补了静态语言在动态行为上的不足.同时,反射还是实现元编程的重要手段. 和 C 数据结构一样,Go 对象头部并没有类型指针,通过其自身是无法在运行期获知任何类型相关信息的.反射操作所需要的全部信息都源自接口变量.接口变量除存储自身类型外,还会保存实际对象的类型数据. func TypeOf(i interface{

  • linux Shell学习笔记第一天

    以下是Shell学习1-2天学习笔记 ---------我是分隔符--------- 硬件去执行, 内核与硬件之间进行操作. 命令解析器. shell脚本的组成元素 系统命令 文本处理工具(sort.grep.sed.awk-) 变量 条件判断 环循结构 函数 Shell Scripts Center(SSC) ---------------------------- 非负Grep / awk 几天. 统计Wc Sort 排序 sort|head / sed字段处理 awk 数据区域判断 awk

随机推荐