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 基础 & 元编程的更多相关文章
- Ruby元编程:动态添加类属性及其实际应用
上个星期测试道的Monkey老师和我聊到测试用例参数过多的问题,其实这样的问题在我这里也同样经历过.比如我的测试用例必须面对不同的测试环境,每个环境有无数的参数,开发的最初阶段,因为参数少,所以就放在 ...
- C++值元编程
--永远不要在OJ上使用值元编程,过于简单的没有优势,能有优势的编译错误. 背景 2019年10月,我在学习算法.有一道作业题,输入规模很小,可以用打表法解决.具体方案有以下三种: 运行时预处理,生成 ...
- 3-11 《Ruby元编程》第4章block块 3-12
第4章代码块blocks 基础知识 作用域:用代码块携带variables through scopes 通过传递block给instance_eval方法来控制作用域. 把block转换为Proc, ...
- 3-8《Ruby元编程》第二章对象模型
<Ruby元编程> 第二章 对象模型 类定义揭秘inside class definitions: class关键字更像一个作用域操作符,核心作用是可以在里面随时定义方法. [].meth ...
- Ruby元编程:单元测试框架如何找到测试用例
前几天看了Google Testing Blog上的一篇文章讲到C++因为没有反射机制,所以如何注册测试用例就成了一件需要各显神通的事情.从我的经验来看,无论是Google的GTest还是微软的LTM ...
- 201707《Ruby元编程》
元编程不过是编程--经典必读 作用域(绑定) 打破作用域门的方式 对象模型图 七条规则 法术手册 作用域(绑定) 改变作用域的关键字, 分别是module,class和def.我们称为作用域的门(sc ...
- 翻译 - 元编程动态方法之public_send
李哲 - MAY 20, 2015 原文地址:Metaprogramming Dynamic Methods: Using Public_send 作者:Friends of The Web的开发者V ...
- Groovy元编程简明教程
同函数式编程类似,元编程,看上去像一门独派武学. 在 <Ruby元编程>一书中,定义:元编程是运行时操作语言构件的编程能力.其中,语言构件指模块.类.方法.变量等.常用的主要是动态创建和访 ...
- C++模板元编程(C++ template metaprogramming)
实验平台:Win7,VS2013 Community,GCC 4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++ 模板给 C++ 语言提供了元编程的能力,模板使 C++ 编程变得 ...
随机推荐
- selenium 参数设置-window.navigator.webdriver
selenium 参数设置 selenium启动chrome基本上与真实环境类似,但有一些变量还是不一样,需要注意. 有些网站通过这些参数识别爬虫. window.navigator.webdrive ...
- 牛茶冲天的ip命令
一.修改二层链路相关设置 1.修改网卡名称(修改前要先停止) ip link set eth0 name testname 2.修改网卡地址 ip link set eth0 address xxx ...
- 十 Spring的AOP的底层实现:JDK动态代理和Cglib动态代理
SpringAOP底层的实现原理: JDK动态代理:只能对实现了接口的类产生代理.(实现接口默认JDK动态代理,底层自动切换) Cglib动态代理(类似Javassist第三方的代理技术):对没有实现 ...
- 吴裕雄 Bootstrap 前端框架开发——Bootstrap 显示代码:电脑程序输出: Sample output
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- c数据结构线性表的总结
1:连表的特点 1.链表是一种存储结构,用于存放线性表 2.连表的结点是根据需要调用动态分配内存函数进行分配的,因此链表可随需要伸长缩短 在腰存储的数据个数未知的情况下节省内存. ...
- Django:cookie和session相关问题
http://www.cnblogs.com/fnng/p/3750596.html http://www.cnblogs.com/chenchao1990/p/5283725.html
- CDC学习
最近在建立CDC环境,在网上看到一些不错的学习链接,粘贴如下: 1.https://blog.csdn.net/u011729865/article/details/52931366 属于https: ...
- [理解] Linux 作为一个服务器是怎样的存在 (一)
长期以来我就一直有一个疑问, 为什么当我们选择使用服务器的时候都会选择 Linux 作为操作系统, 以至于只要说到服务器就会不由自主的想到Linux, 那么Linux到底是什么呢? 当然我也不会妄谈天 ...
- 通过命令行提交更新代码到gitlab上
解决方法: 1.打开命令行的窗口,定位到项目所在的路径. 2.输入:git status,敲回车查看代码是否有更新,有更新的话会出现文件改变的文件名.(红色的) 3.输入:git commit -a ...
- 简析ThreadLocal原理及应用
简析ThreadLocal原理及应用 原创: 东晨雨 JAVA万维猿圈 4月17日 ThreadLocal的源码加上注释不超过八百行,源码结构清晰,代码也比较简洁.ThreadLocal可以说是Jav ...