201706 Ruby 基础 & 元编程
Posted 观星客的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了201706 Ruby 基础 & 元编程相关的知识,希望对你有一定的参考价值。
- yield
- methods、proc、lambda、block
- 闭包(用proc延长变量的生命周期)
- method_missing终止对祖先链(ancestors)的查找
- p、puts、print
- 正则、字符相关特殊符号
- Module 是 Class 的基类,但Module不能实例化,并且只能与类一同使用
- 异常
-
yield
所有的"方法(methods)"隐式跟上一个"块(block)"参数。
块参数也可以明确给定,形式就是在参数前面加一个"&",比如 def fn(arg1, arg2, &block) end,其中的 &block 就是明确给定的块参数。
块参数的动作,可以通过调用 call() 方法执行,还可以用 yield 来执行 —— yield 其实就是一个语法糖。
所以以下几种写法常常是等价的:
#method receives an invisible block argument def foo1() yield 1 end #specify it explicitly def foo2(&block) yield 1 end #yield is equal to block.call def foo3(&block) block.call(1) end #function call foo1 {|x| puts x} # => 1 foo2 {|x| puts x} # => 1 foo3 {|x| puts x} # => 1
注意事项
method 定义中 &block 参数必须在最后yield self
在一个对象中,self 表示是一个当前对象的引用。
所以,常见的 yield self if block_given? 中的 self 就和其它地方使用 self 一样,没什么特殊的。
Proc
前面说到所有方法都可以隐式或显式指定一个块参数,那么块参数到底是什么呢?
答案是 Proc 对象,一个具有 call 方法的对象。
Proc 对象的定义有几种形式:
- 直接使用 {}
- 使用 Proc.new {}
- 使用 proc {}
- 使用 lambda {}
#yield is equal to block.call def foo(&block) puts block.class puts block.to_s yield 1 end #function call # Proc created using {} syntax foo {|x| puts x} # => Proc # => #<Proc:0x00000000e0b140@(ruby):9> # => 1 # Proc created with the "proc" keyword. Note & syntax when calling. my_proc = proc { |n| puts n } foo(&my_proc) # => Proc # => #<Proc:0x00000000e0b140@(ruby):12> # => 1 # Proc creates with Proc.new my_proc = Proc.new { |n| puts n } foo(&my_proc) # => 1 # => Proc # => #<Proc:0x00000000e0b140@(ruby):16> # => 1 # Proc created with the "lambda" keyword. Nearly same thing. my_proc = lambda { |n| puts n } foo(&my_proc) # => Proc # => #<Proc:0x00000000e0b140@(ruby):20 (lambda)> # => 1
yield带参数
def many_yields yield(:peanut) yield(:butter) yield(:and) yield(:jelly) end def test_methods_can_call_yield_many_times result = [] many_yields { |item| result << item } # result作用域 assert_equal [:peanut, :butter, :and, :jelly], result end
rails中:yield 和 content_for
在View布局中,yield 标明一个区域,渲染的视图会插入这里。最简单的情况是只有一个 yield,此时渲染的整个视图都会插入这个区域:
<html> <head> </head> <body> <%= yield %> </body> </html>
布局中可以标明多个区域:
<html> <head> <%= yield :head %> </head> <body> <%= yield %> </body> </html>
视图的主体会插入未命名的 yield 区域。若想在具名 yield 区域插入内容,要使用 content_for 方法。
<% content_for :head do %> <title>A simple page</title> <% end %> <p>Hello, Rails!</p>
套入布局后生成的 HTML 如下:
<html> <head> <title>A simple page</title> </head> <body> <p>Hello, Rails!</p> </body> </html>
methods、proc、lambda、block
- block和proc都不检查参数,methods和lambda会检查参数
- block和proc是两种不同的东西, block有形无体,proc可以将block实体化, 可以把&p看做一种运算,其中&触发p的to_proc方法,然后&会将to_proc方法返回的proc对象转换成block 。
- &p是block, p是proc,不到万不得已的情况下不要显式地创建proc
- lambda是匿名方法, lambda和proc也是两种不同的东西,但是在ruby中lambda只能依附proc而存在,这点和block不同,block并不依赖proc。
- lambda和proc之间的区别除了那个经常用做面试题目的经典的return之外,还有一个区别就是lambda不能完美的转换为block(这点可以通过f3和f4执行的过程得证),而proc可以完美的转换为block,注意,我说的lambda指的是用lambda方法或者->符号生成的proc,当然和方法一样lambda是严格检查参数的,这个特点也和proc不一样。
闭包(用proc延长变量的生命周期)
有两只途径实现:
闭包创建了它所需要的所有变量的一个备份, 因此是这些副本随着闭包传递.
闭包 延长了它所需要的所有变量的生命周期. 没有复制变量, 而是保留了它们的引用, 而且变量本身不可以被垃圾回收器回收掉.
如果语言支持第一种方式, 那么如果我们创建两个或者更多闭包来访问相同的变量, 每个闭包被调用时都有自己单独对变量的拷贝. 如果语言支持第二中方式, 所有的闭包都引用同一个变量, 它们实际上处理的就是同一变量. Ruby 就是这么做的.看下面的例子:class SomeClass def initialize(value1) @value1 = value1 end def value_incrementer lambda { @value1 += 1 } end def value_printer lambda { puts "value: #{ @value1 }"} end end some_class = SomeClass.new(2) incrementer_closure = some_class.value_incrementer printer_closure = some_class.value_printer 3.times do incrementer_closure.call printer_closure.call end 运行结果: #=> value: 3 value: 4 value: 5
method_missing终止对祖先链(ancestors)的查找
什么情况下需要使用?
p、puts、print
class T def initialize(i) @i = i end def to_s @i.to_s end end t = T.new 42 puts t => 42 p t => #<T:0xb7ecc8b0 @i=42>
- p、puts 自动换行,print不自动换行
- p有返回值, puts、print返回nil
正则、字符相关特殊符号
- %{String} 用于创建一个使用双引号括起来的字符串
- %Q{String} 用于创建一个使用双引号括起来的字符串
- %q{String} 用于创建一个使用单引号括起来的字符串
- %r{String} 用于创建一个正则表达式字面值
- %w{String} 用于将一个字符串以空白字符切分成一个字符串数组,进行较少替换
- %W{String} 用于将一个字符串以空白字符切分成一个字符串数组,进行较多替换
- %s{String} 用于生成一个符号对象
- %x{String} 用于执行String所代表的命令
大写的支持变量替换
Module 是 Class 的基类,但Module不能实例化,并且只能与类一同使用
异常
throw 和 catch功能类似goto 在ruby不是用来处理异常的
begin f = File.open("ruby.txt") # .. continue file processing rescue ex => Exception # .. handle errors, if any ensure f.close unless f.nil? # always execute the code in ensure block end
元编程
元编程能力能够让程序员编写在运行时动态生成的代码。
它的线程功能使得程序员有一种优雅的的方式编写多线程代码。
它的钩子方法能让程序员在程序运行时扩展它的行为。《Ruby元编程》待读
元编程简述类打开,添加新方法,如何删减方法
删除类方法的途径有2个,一个是调用Module#undef_method方法,一个是调用Module#remove_method方法。
创建单例方法
class A end a = A.new def a.f1 puts \'f1\' end class << a def f2 puts \'f2\' end end
创建类方法
class SelfTest def self.test puts "Hello World with self!" end end class SelfTest2 def test puts "This is not a class static method" end end SelfTest.test # works fine SelfTest2.test #error class TestMe def TestMe.test puts "Yet another static member function" end end TestMe.test # works fine class MyTest class << self #创建/打开一个元类(MyTest是class实例),为其添加单例方法 def test puts "This is a class static method" end end end MyTest.test # works fine MyTest.new.test # error #其他方式创建类方法 class A end (class << A; self; end).class_eval do def you p \'you\' end end A.singleton_class.class_eval do def fun p \'fun\' end end
变量归属
class B @a=1 #B.instance_variables @@b=2 #B.class_variables def initialize @c = 3 #b.instance_variables @@d = 4#B.class_variables end class << self @e = 5 #B.singleton_class.instance_variables @@f = 6#B.class_variables end end
self
self作用域
class A class<<self # self # end end
不能用于私有方法:对私有方法的调用只能针对隐式对象。
class A def p1 self.s1 end def p2 s1 end private def s1 puts "private def " end end a = A.new a.p1 #error a.p2 # ok a.s1 #error
send 是 Object 类的一个公共方法
- 可用于访问一个类的私有方法
魔法太多,如何调试
以上是关于201706 Ruby 基础 & 元编程的主要内容,如果未能解决你的问题,请参考以下文章