当前位置:网站首页>辨析 Ruby 中的 Method 与 Proc
辨析 Ruby 中的 Method 与 Proc
2022-07-26 15:44:00 【飞驰的西瓜】
Ruby is simple in appearance, but is very complex inside, just like our human body. — Matz https://www.ruby-lang.org/en/about
Ruby 与 Python、Scala 类似,在一切皆是对象(Seeing Everything as an Object)的基础上,支持函数式编程,这意味着函数是一等成员,可以作为参数传入,也可以作为函数值返回。
但是,Ruby 中的函数并没有其他动态语言中那么简单,它提供了 Method 与 Proc 两个类来表示函数的概念,对于这两个类的区别无论是官方文档还是 Stackoverflow 上的问题,解释的都非常模糊。在其他语言函数很习以为常的用法在 Ruby 中却行不通,就其原因还是不清楚这两个类的区别,希望这篇文章能够帮助大家理解好 Ruby 中的“函数”概念,做到深入浅出,与其他函数式语言融会贯通。
Block-oriented Programming
Ruby 中代码块最常见的形式既不是 Proc 也不是 Method,而是 block。比如:
# 遍历 Range/Array 等
(0..10).each do |num|
puts num
end
# 读取文件
File.foreach('README.md').with_index do |line, line_num|
puts "#{line_num}: #{line}"
end
# 遍历文件
Dir.glob('*.rb') {|ruby_src| puts "found #{ruby_src}"}上面示例演示了block的两种字面量(literal)形式,非常方便简洁。但有一点需要注意,block 仅仅是 Ruby 提供的一语法糖衣,并不把其赋值给某一变量。如果自定义函数需要调用传入的block,需要采用yield方式。
# 在 Array 类中添加自定义函数
class Array
def my_each
0.upto(size) do |i|
yield self[i]
end
end
end
%w(a b c).my_each do |item|
puts item
end面向函数式的 Proc
block 的优势是简洁,但是有个缺点就是无法复用,因为并不存在block类型。但在其他语言中,函数名可以随意传递,下面举一 Python 的例子:
def myinc(x):
return x + 1
map(myinc, [1,2,3]) # => [2, 3, 4]
map(myinc, [4,5,6]) # => [5, 6, 7]Ruby 中与其对应的是过程(Proc),与上面功能等价的 Ruby 代码为:
myinc = Proc.new {|num| num + 1}
# 或下面两种方式
# myinc = proc {|num| num + 1}
# myinc = lambda {|num| num + 1}
[1,2,3].map(&myinc)上面代码最关键的是&myinc中的&,由于 map 函数后面可以跟一个 block,所以需要把 Proc 转为 block。
当
&符号出现在函数参数列表中时,会把其后面的参数转为 Proc,并且把转化后的参数作为 block 传递给调用者。 http://stackoverflow.com/a/9429972/2163429
我这里有个更好的理解大家可以参考:
&在C语言中为取地址符,Ruby 中的函数参数后面可以跟一个 block,由于这个 block 不是参数的一部分,所以没有名字,这很理所当然可以把 block 理解为一内存地址,block_given?函数可以检查这个block是否存在。&myinc可以理解为取 Proc 的地址传给 map 函数。
[1,2,3].map(myinc)
# 这种写法会报下面的错误
# in `map': wrong number of arguments (given 1, expected 0) (ArgumentError)所以,Ruby 中的 Proc 和其他动态语言的函数是等价的,下面再举一例说明
def myfilter(arr, validator)
arr.each do |item|
if validator.call(item)
puts item
end
end
end
myfilter([1,2,3,4], lambda {|num| num > 3}) # 输出 4
# 此外, 还可以在定义 myfilter 时,利用 & 将最后的 block 转为 Proc
def myfilter(arr, &validator)
arr.each do |item|
if validator.call(item)
puts item
end
end
end
myfilter([1,2,3,4]) {|num| num > 3}
# 输出 4proc vs. lambda
上面介绍过,Proc 有两种字面量形式:
myinc = proc {|num| num + 1} # 与 Proc.new 等价
myinc = lambda {|num| num + 1}这两种形式的 Proc 有以下两点不同:
proc形式不限制参数个数;而lambda形式严格要求一致
proc中的return语句对调用方有效;而lambda仅仅对其本身起作用
面向对象的 Method
Ruby 中使用def定义的“函数”为Method类型,专为面向对象特性设计,面向对象更一般的说法是消息传递,通过给一对象发送不同消息,对象作出不同相应,这一点与 SICP 第三章的内容不谋而合。
class Rectangle
def initialize(width, height)
@width = width
@height = height
end
def area
@width * @height
end
end
rect = Rectangle.new 10, 20
# 传统方式
puts rect.area
# 消息传递方式
puts rect.send :area由于 Ruby 中方法名表示的是调用,所以一般可用与方法同名的 Symbol 来表示。
puts rect.method(:area)
#<Method: Rectangle#area>可以通过 Method 的 to_proc 方法可以将 Method 转为功能等价的 Proc。比如:
def myinc(num)
num + 1
end
[1,2,3].map(&method(:myinc))
# => [2,3,4]
# 在 Ruby 源文件的顶层定义的函数属于 Object 对象,所以上面的调用相当于:
# [1,2,3].map(&Object.method(:myinc))总结
block为 Proc 的语法糖衣,用于单次使用时Proc专为函数式编程设计,与其他动态语言的函数等价Method专为面向对象设计,消息传递的第一个参数
弄清 Method 与 Proc 的区别后,不得不欣赏 Ruby 语言设计的巧妙,兼具函数式与面向对象的精髓。实在是程序员必备利器。
参考
边栏推荐
- University rankings in Beijing
- 大型仿人机器人整机构型研究与应用
- Digital warehouse: iqiyi digital warehouse platform construction practice
- tensorboard多个events文件显示紊乱的解决办法
- 泰山OFFICE技术讲座:WORD的缩放比例与显示略有差异
- Zynq PS + PL heterogeneous multicore Case Development Manual of Ti C6000 tms320c6678 DSP + zynq-7045 (1)
- Kalibr calibration realsensed435i -- multi camera calibration
- [five minute paper] reinforcement learning based on parameterized action space
- Tutorial (7.0) 05. Issue forticlient * forticlient EMS * Fortinet network security expert NSE 5 through forticlient EMS
- SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
猜你喜欢

PS + PL heterogeneous multicore case development manual for Ti C6000 tms320c6678 DSP + zynq-7045 (4)
.NET 手动获取注入对象

Digital warehouse: iqiyi digital warehouse platform construction practice

Musk was exposed to be the founder of Google: he broke up his best friend's second marriage and knelt down to beg for forgiveness

【DSCTF2022】pwn补题记录

parker电磁阀D1VW020DNYPZ5

Tutorial (7.0) 05. Issue forticlient * forticlient EMS * Fortinet network security expert NSE 5 through forticlient EMS

数仓:爱奇艺数仓平台建设实践

Delta controller rmc200

Refuse noise, the entry journey of earphone Xiaobai
随机推荐
Summary of QT plug-in development -- add plug-in menu in the main interface
“卡片笔记法”在思源的具体实践案例
原来卡布奇诺信息安全协会是干这个的呀,一起来看看吧。
Glyphicons V3 字体图标查询
Enterprise digital transformation needs in-depth research, and it cannot be transformed for the sake of transformation
HaWe screw cartridge check valve RK4
A comprehensive review of image enhancement technology in deep learning
教大模型自己跳过“无用”层,推理速度×3性能不变,谷歌MIT这个新方法火了...
德国EMG易安基推动器ED301/6 HS
OSPF综合实验
组件化开发基本规范、localStorage 和 sessionStorage、对象数据转基本值、原型链使用
bucher齿轮泵QX81-400R301
山西阳泉一煤矿发生致1人死亡安全事故,被责令停产整顿
German EMG e-anji thruster ed301/6 HS
Detailed explanation of nat/napt address translation (internal and external network communication) technology [Huawei ENSP]
81. (cesium home) cesium modifies the gray background (default blue)
数仓:数仓建设中的数据建模和日志体系
八叉树建立地图并实现路径规划导航
kalibr标定realsenseD435i --多相机标定
13年资深开发者分享一年学习Rust经历:从必备书目到代码练习一网打尽