Meta Ruby-ruby元编程


Moday: The Ojbect Model

  • Monday with Bill
  • Open Classes
    • Inside Class Definitions
    • The Problem with Open Classes
  • The Truth About Classes
    • What's in an Object
    • Classes Revisited
    • Constants
    • Objects and Classes Wrap-up
    • Another Learning Opportunity
  • Quiz: Missing Lines
    • Quiz Solution 
  • What Happens When You Call a Method?
    • Method Lookup
    • Kernel Module
    • Method Execution
  • Quiz: Trangle of Modules
    • Quiz Solution
  • Object Model Wrap-up

Tuesday: Methods
  • A duplication Problem
    • The Legacy System
    • Double, Treble... Trouble
  • Dynamic Methods 我的导师告诉我,当你调用一个方法时,实际上是给一个对象发送了一条消息。
    • Calling Methods Dynamically
      • obj.my_method(3) 这个 . 就是调用方法的标记,使用Object#send方法可以替换这个点, obj.send(:my_method, 3) 第一个参数可以是符号或者字符串,为什么要用send?因为可以将方法名作为一个参数,在代码运行的最后阶段才决定调用哪个方法。这种技术称为:Dynamic Dispatch 动态派发
    • x = :this_is_a_symbol,符号和字符串的区别?大多数情况下,符号表示事物的名字,尤其是和元编程相关的名字,比如方法名: 1.send(:+, 2), String.to_sym or String#intern(),  反向调用是Symbol#to_s or Symbol#id2name();
    • OpenStruct
    • 动态派发技术给每个 key 赋值。  YAML.load_file(conf.rc).each {|k, v|  conf.send("#{k}=", v)}
    • Test::Unit 里的动态派发判断哪些方法是测试方法: method_names = public_instance_methods(true); tests=method_names.delete_if {|method_name| method_name !~ /^test./} 这种方法优势成为: Pattern Dispatch 模式派发。
    • 动态定义方法: Module#define_method();  define_method :my_method {|my_arg| my_arg *3};类的实例方法,,动态方法=Dynamic Method; Dynamic Class Method
    • Refactoring the Computer Class: 提炼公共的代码为一个方法.
    • introspection:  data_source.methods.grep(/^get_(. )_info$/) { Computer.define_component $1 }
  • method_missing() 这个非常特殊的方法,Ghost Method, 它存在kernel#method_missing,  nick.send :method_missing, :my_method   ,这句code是ruby解释器找不到方法是做的事。
    • Overriding method_missing() 每个来找method_missing()方法的都带着方法和参数过来的。  
    • def method_missing(method, *args)
    •     puts "You called: #{method}  (#{args.join(', ')} )"
    •     puts "(You also passed it a block)" if block_given?
    • end
    • Ghost Methods 幽灵方法,例子Gem包:ruport
    • 如果别人问你一个你不知道的问题,你就问:你为什么要问这个问题?  。。。接受者并没有相对应的方法,这就是个Ghost Method.
    • return as($1.to_sym, *args, &block) if id.to_s=~ /^to_(.*)/); return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/
    • rows_with_country("France")虽然不过是个语法糖,但也有实际好处:如果定义新的输出格式,就会自动获得相对应的方法。
    • attribute = name.to_s; if attribute =~ /=$/ { @attributes[attribute.chop] = args[0] }
  • Dynamic Proxies 动态代理
    • http://api.flickr.com/services/rest/?method=flickr.people.findByUsername&username=ducandavidson&api_key=your API key here
    • method=flickr.tags.getListUser() 把Flickr API方法中的点号.变成下划线_. 这样就可以变成一个合法的Ruby方法名。 Flickr#tags_getListUser()都是幽灵方法。
    • request(method_id.id2name.gsub(/_/, '.'), params[0])
    • delegate;  require 'delegate'; 助手被DelegateClass(助手类);  管理类 < DelegateClass(助手类),  管理处理不了的方法,自动调用助手类的方法处理。
    • method_missing() 会把方法转发给别的对象的
  • Refactoring the Computer Class(Again)
    • @data_source.respond_to?("method")  ; @data_source.send("method", args[0]);
    • respond_to? 方法,判断不了Ghost方法method_missing()...里面的方法无法判断,这是需要overload respond_to?方法。
    • const_missing()
  • Quiz: Bug Hunt
    • method_missing方法使用要小心,谨慎。
  • More method_missing()
    • When Methods Clash冲突时候,如果存在真实方法,则不会调用method_missing方法。
    • 这里要用到 undef_method方法,取消继承链条里面的别的方法。
    • undef_method m unless m.to_s =~ /method_missing|respond_to?/  还有个Object中的__id__ 和 __send__不能取消, may cause serious problem.
    • instance_methods.each do |m|
    •   undef_method m unless m.to_s =~  /^__|method_missing|respond_to?/ 这些特征的方法保留,其它的方法 取消。
    • end
  • Wrapping It Up
    • p BasicObject.instance_methods => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__] ,从BasicObject继承来的类自动成为白板类 BlankClass
    • 发现方法之间的重复性

第三章 代码库
Wednesday: Blocks
block控制作用域的手段。作用域表示:哪些代码可以看到哪些变量和方法。 可调用对象: block, proc, lambda. 存储一个块,供后续使用。
块是继承自“函数式编程语言 functional programming language” ,对象、方法、块。
  • How to Handle Hump Day 如何读过驼峰日
    • Today‘s Road map
      • Block块   =>   Scope作用域  =>  Closure闭包  => instance_eval()  =>  block  to Proc and lambda  be  callable object  块变可调用的对象。
    • Back to the Basics
      • 只有调用一个方法时,才能定义一个块: a_method(1,2) {|x, y| (x +y )*3},  前面是调用方法,后面紧跟着代码块:可以是{...},或者 do...end
      • Kernel#block_given?
      • return yield if block_given?
    • Quiz:Ruby#
      • using 关键字,里面的代码执行完后会自动释放。
      • The Challenge and Quiz Solution: begin ...  rescue....rescue ...rescue.... else  ...  ensure ...
      • module Kernel
      •   def using(resource)
      •     begin
      •       yield
      •     ensure
      •       resource.dispose
      •     end
      •   end
      • end
  • Closures 闭包
    • 闭包也是块,代码块中的变量是定义的时候绑定的当时环境的变量,在被调用的方面里面的变量,这个代码块看不见,也就称为闭包closure.
    • 所有绑定寄居的场所--作用域-scope, 绑定是对象的局部变量,实例变量,self。
  • Scope作用域
    • 站在一个有实例变量和方法的self对象中,脚下一堆局部变量,远处一棵挂满常量的树,更远处还有一组全局变量。
    • Scope Gate:  class, module, def ; class and module定义是会被执行,def执行时才会。
    • 全局变量: $开头的
    • 顶级实例变量: @开头的
  • Flattening the Scope 扁平化作用域。
    • nested lexical scopes 嵌套文法作用域  [ˈleksɪkl] 词汇的
    • flattening the scope 扁平化作用域
    • Flat Scope 扁平作用域
      • class =》 Class.new;   def => define_method; ,通过这种替换,穿越作用域门。
    • 共享作用域
      • 在方法里面,通过Kernel.send :define_method, :my_method {} 来定义方法。  这些动态派发(Kernel.send)方法,共享变量的技巧称为 共享作用域。
  • Scope Wrap-Up 作用域小结, 穿越作用域
    • class =》 Class.new
    • module => Module.new
    • def => Module#define_method()

  • instance_eval()
    • Object#instance_eval()
    • instance_eval(),  eval 是评估的意思,实例评估,评估就要进入实例的内部,,进入内部去探测,这里就有个新名词:探针,谁是探针?instance_eval()带的代码块就是探针,因为这段代码就像探针一样进入到了instance的内部。,  instance_eval待的这段代码块被称为: 上下文探针 Context Probe, 
    • instance_exec() 是升级版, 可以给进入对象内执行的块,带上参数。
    • Breaking Encapsulations  打破封装
      • 这个很有趣,也就是给对象里面增加实例变量,而又不用重新去定义类, someobject.instance.eval {@options =  something } 这个太厉害了,算是动态定义了实例变量了吗?
      • 这个上下文探针的应用感觉已经超出了名字的范围,不仅仅是探针,毕竟探针只是探测是只读的,这个是修改了。。
    • Clean Room 洁净室
      • Clean Room就是干净的房间,进来就是为了执行代码块Block。
      • 这又是一个有趣的应用,洁净室里面只有一些方法,就好比是一个没有署名的工具,都可以用,
      • 这个 Clean Room 是不是像 云函数的概念呢? 把代码提交上到云上执行, 提交的代码不会更改云函数。
      • 代码换一种写法,让这个代码更具有层次感了。
      • 代码块就是闭包,它在调用方法的时候被定义,通过block_given?判断是否存在,通过yield(&block)调用,它可以去当探针,也可以用于洁净室。
  • 可调用对象 Callable Object
    • 代码块有很多个称呼了,如探针,闭包。这里还可以转换成一个对象:proc,lambda
    • Proc.new {|x| x+1}
    • lambda {|x| x -1}
    • Proc {|x| x -1}
    • 块就像是方法的额外的匿名参数。yield运行一个块。
    • 如何把块传给另外一个方法? 需要给这个匿名参数赋予一个&开头的名字,且只能放在最后。
    • def teach_mach(a, b, &operation)  &operation 就表示一个Proc对象。这也是&操作符的真正含义。
    • &操作符的真正含义:这是一个Proc对象,我想把它当做一个块来使用。
    • 简单的去掉&操作符,就能再次得到一个Proc对象。
    • Proc对象就可以用 Proc.call()来执行。 通过call来调用,这种技术称为:延迟执行: Defered Evaluation
    • def my_method(greeting)
    •   puts "#{greeting}, #{yield}"
    • end
    • my_proc = Proc {"Bill"}
    • my_method("Hello", &my_proc)   =>  将my_proc当做块来使用&my_proc
    • HighLine例子
    • name = h1.ask("Name?", lambda {|x| x.capitalize })
    • puts "Hello, #{name}"
    • 先把Proc传给Question类的一个对象,并把这个Proc当成一个实例变量存储起来,在收集完用户输入后,这个Question对象会把用户的输入传递给这个Proc对象。这也是延迟执行 Defered Evaluation
    • Proc 和Lambda的区别
      • proc更像一段程序,return返回的是定义它的作用域。 传递给他的参数也数量也是可以变化的,就像你是否定义了几个变量。
      • Lambda 像一个对象, return返回的是lambda本身, 传递给他的参数也是固定不可变的。定义的是2个参数,那就传2个参数。
      • 鉴于此,大家爱用lambda多一点,他还有种简写的方式:
      • p = ->(x) {x + 1}  代替: p = lambda {|x|  x+ 1}
    • Methods Revisited重访方法。
      • lambda就是个方法,
      • 普通的类的对象的方法,可以通过 p = obj.method :my_method  的方式将这个my_method变成lambda 。 p.call() = object.my_method(). 这里不是完全相等的,lambda是闭包,没有obj所在的环境下的装备(绑定),那么可以将这个目前为lambda的 p卸掉装备,unbound = p.unbind, 卸掉装备的这个p变成了真正的lambda,但是他又不是完整的lambda,是一个没有灵魂的专属残片。纯正的lambda是有标准接口的一个手臂,可以用来接入到设备上,从对象过来的p是从设备上扣下来的还连着控制线手臂,还能够被obj使用,现在通过unbind,断开了和obj的链接,成了个孤件,再给他接到同类的上面,又可以用了。p = unbound.bind(another_object); p.call
      • Method#to_proc
      • Object#method :my_method     => lambda
      • define_method 把块转换成方法
    • Callable Objects Wrap-Up可调用对象小结
      • proc
      • lambda
      • 方法
    • 领域专属语言 Writing a Domain-Specific Language
      • def event(name)
        • puts "ALERT: #{name}" if yield
      • end
      • Dir.glob('*events.rb').each {|file| load file}
      • test_events.rb
      • event “。。。” do
      •   true
      • end
    • 共享event

lambda {
  setups = {}
  events = {}
  Kernel.send :define_method, :event do |name, &block|
    events[name] = block
  end
  Kernel.send :define_method, :setup do |&block|
    setups << block
  end

  Kernel.send :define_method, :each_event do |&block|
    events.each_pair do |name, event|
      block.call name, event
    end
  end

  Kernel.send :define_method, :each_setup do |&block|
    setups.each do |setup|
      block.call setup
    end
  end

}.call

Dir.glob('*events.rb').each do |file|
  load file
  each_event do |name, event|
    env = Object.new
    each_setup do |setup|
      env.instance_eval &setup
    end
    puts "ALERT #{name}" if env.instance_eval &event
  end
end
 

Kernel.send : 动态派发
:define_method,  块变方法
lambda {...}.call 延迟执行。
env.instance_eval &setup  执行环境绑定
instance_eval 进入实例内执行。

类是一个加强的模块
Class Definitions Demystified 类定义揭秘
Inside Class Definitions 深入类定义
类也可以是self, 当前类 The Current Class
怎么样才能在不知道类的名字的情况下打开一个类呢?需要一种新的方式,它不需要使用class关键字就能修改当前类。答案就是class_eval()
Module#class_eval()方法会在一个已经存在的类的上下文中执行一个块:
def add_method_to(a_class)
  a_class.class_eval do 
    def m; 'Hello!'; end
  end
end

add_method_to String
"abc".m   #=> "Hello!"

当前类
类里面的方法1里面再定义方法2,方法2的self不是类,但是还是有当前类。
在顶级作用域中定义方法的时候,这个方法会成为Object类的实例方法。

instance_eval()和class_eval()区别很大
instance_eval仅仅会修改self
class_eval()会同时修改self和当前类,实际上是打开了该类,就像class关键字做的一样。

阅读量: 733
发布于:
修改于: