Mark24
笔记:Ruby可调用对象实验
观点
本来我的观点应该放在最后,但是这个实验不太复杂。而我下次可能也是直接看上面的。所以我简单写一点。
Ruby的内部无疑是很复杂的,《Ruby原理剖析》虽然已经写的很好了,然是依然是建立在大幅简化的条件下进行讲解。即使看完我们也许依然很难真正理解Ruby的工作原理。具体的还需要查询源码。
不过我觉得,最近看书自己存在一个误区,或者获得了一个新的想法。
我们想要学习和理解一个工具,更看重的是他的原理和最后黑盒的表达罢了。据个例子就像开车,你大概知道了内燃机、电动机原理,那么你踩了油门车子起步,踩了刹车车子停止。这个技能就算学会了。至于汽车内部有多么复杂,用了什么黑科技保驾护航,研究他们那是另一个课题了。
类似,我这几天要么在入门书籍里面的一些不严谨的比喻、概念里面建立抽象的心理模型、抑或是进入《剖析》这种又是大量细节的内容里。最终有一种迷失感。
但是这件事其实是没那么复杂的。只需要跳出来思考。
我沉淀的一个观点是 —— 我们应该理解他的意图。
Ruby的设计再复杂也伴随着一个目的。从Ruby的表现和应用实际上他是想表达一个简洁、又自动化搜索(深邃)的东西。
其他的语言诸如JS是一种很混乱的存在。Ruby的作者Matz是语言的狂热粉,他写过很多书表达自己的设计思考还有对其他语言的观察。
Ruby的东西是可以跳出来思考的——把你自己站在设计者的角度、抑或是使用者的角度,去思考合理性。
比如可调用对象这部分。后面会总结,也是书本的摘要。我这里就直接简化的理解:
Ruby里面 Class 其实是一种封装结构,是为了一种面向对象的风格。
Ruby里面 承担起主要逻辑(就像JS里面的function)其实是 块(Block)
块 就是 do ~ end 这部分代码。他像代码一样工作。在定义的词法作用域里工作。
Proc是底层支持块的更朴素的类,相对来说一切更加宽松(或者说方法少。越继承方法约多约束也越多)。他也是在词法作用域里工作。
块和Proc是相似的。
其实就可以理解为 块 是我们书写的代码。 Proc是一段代码对象。
Lambda就是 Proc的对象,表现的就像匿名函数。更确定的存在。
Method可以说就是一个具名函数。
Lambda和Method是相似的。
其实就可以理解为 Lambda是匿名函数,Method是具名函数。
可调用对象总结
- 代码块
代码块不是真正的对象,但是他们是 可调用的,在定义他们的作用域中执行。
- proc
Proc类跟代码块一样,也在定义自身的作用域中执行
- lambda
也是Proc对象,但是他跟普通的proc有细微差别,他跟块和proc一样都是闭包,因此也在自身的作用域中执行
- 方法
绑定于一个对象,在所绑定的对象作用域中执行。他们也可以和这个作用于解除绑定,然后再重新绑定到另一个对象的作用域。
在方法和lambda中,return 语句从可调用对象中返回。(自身可作为函数看待)
在块和proc中,return从定义可调用对象的原始上下文中返回。(词法代码)
lambda、方法对参数严格。proc宽松。
Proc 和 Lambda 的差异性
1. return 之处返回
Lambda中 return仅仅表示从这个 lambda 中返回
其实 Lambda 就是一个匿名函数(类比JS里面的function(){}
行为保持一致)
lambda_obj = lambda { return 10 }
def double(lambda_obj)
puts lambda_obj.call * 2
end
double(lambda_obj)
# output >>>
# 20
Proc是在定义Proc的作用域中返回(词法位置返回)。简单的说 Proc像一个代码一样,嵌入上下文中。
def double()
p_obj = Proc.new { return 10 }
result = p_obj.call
return result * 2 # 这端代码不可到达
end
puts double()
# output >>>
# 10
Lambda and non-lambda semantics¶
TODO直接中断?????
def double(p_obj)
puts '--in double-'
result = p_obj.call
puts '--result---'
return result * 2 # 这端代码不可到达
end
p_obj = Proc.new { puts '--in proc--'; return 10 }
puts p_obj
result = double(p_obj)
puts result
# output >>>>>
# #<Proc:0x00007fece8912468 call.rb:9>
# --in double-
# --in proc--
对这个结果直接中断我表示不太理解 TODO
2. 参数差别
lambda对参数数量有严格限制。
理解 lambda是一个匿名函数就能很好的理解这个了。函数就要有函数的样子嘛。
Proc则无所谓,参数都会变成数组,超出定义会裁减掉,少于定义会变成 nil
总结
Lambda是你的第一选择,他工作起来就像一个匿名函数。严格的像一个方法。
除非你需要Proc的特性,再去用Proc
Method对象
Ruby的方法貌似可以裁剪。
如果说 JavaScript 其实是可以围绕着函数去做完所有事情的(本质是函数,类只是 数据+函数的封装而已,最本质还是执行栈的函数)。Ruby其实也在围绕函数。
Ruby给我的感觉是,它已经准备好了很多方法(函数)绑定在语言基础上。可以供我们搜寻使用。我们自己建立起来的对象、类的方法,也可以搜寻使用。我们不光要关心我们要写的方法,也可以最大限度组合和利用已经存在的方法。一个不恰当的比喻,C语言是拿着一个镐子去干活,一切都要靠自己写或者引用过来联系,Ruby就是背了一个工具箱。工具箱比较智能,还会根据名字匹配工具(方法)。
class A
def initialize(name)
@name = name
end
def speak_name
puts @name
end
end
obj = A.new('AAA')
m = obj.method :speak_name
m.call
# output >>>>
# AAA
Kernel#method 方法可以获得一个用Method对象表示的方法,可以在以后使用 Method#call来调用。
Kernel#singleton_method 方法可以把单例方法转成Method对象。
Method对象类似 代码块或者lambda,可以通过 Method#to_proc 方法将Method转为 Proc。
另外也可以通过 define_method 方法把代码块转化为方法。
他们之间有个重要区别:
- lambda 在定义他的作用域中执行(闭包)
- Method对象会在他自身所在对象的作用域中执行
自由方法(unbound method)
他跟普通方法类似,不过他从最初定义它的类或者模块中脱离了。
Module#unbind 方法,可以把一个方法变成自由方法。
也可以直接调用 Module#instance_method 获得一个自由方法
module A
def my_method
'hello'
end
end
unbound = A.instance_method :my_method
puts unbound.class
unbound.call
# output >>>>>
# UnboundMethod
# Traceback (most recent call last):
# call.rb:12:in `<main>': undefined method `call' for #<UnboundMethod: A#my_method() call.rb:3> (NoMethodError)
UnboundMethod是无法调用的
虽然不能调用,但是可以把它绑到一个对象上,再次成为一个Method对象
module A
def my_method
puts 'hello'
end
end
unbound = A.instance_method :my_method
puts unbound.class
class B
end
B.send :define_method, :new_my_method, unbound
B.new.new_my_method
# output >>>>
# UnboundMethod
# hello
自由方法,意味着我们连方法都可以进行 “裁剪”操作了。
使用的场景可能很特殊。举个例子比如 我们想要很精细的 调整方法的顺序的时候。可以派上用场。
(JS也可以通过 call、apply来转移方法的执行。很相似)