Day10. 深入了解 Block - Block Part3

在Block系列文章里面

  • Day8 提到了Block, Proc
  • Day9 提到了yield
  • Day10 开始详述Proc物件,以及了解 yield背後的操作原理。

Proc 一共分为proclambda 两种物件。两者功能大致上相同,不过因些微的不同而导致了极大的差别。个人推荐使用lambda,因为其行为与一般的方法差别不大,不过以下会带过Proc, Lambda之间的用法,毕竟虽然平常用不到,但面试却很常被问到。

Proc

首先我们列出Proc的特性

  • 使用 Proc.new {}, proc {} 定义
  • 对於参数的引入没有严格定义
proc = Proc.new { |x| x }
#=> #<Proc:0x00007fd4de0c8248@(irb):8>

proc.call(1)
#=> 1

proc.call
#=> nil
  • proc 里头写 return 就不会往下走了
# 没有return
def apple_machine(adjective)
  proc = Proc.new { |x| x }
  
  [proc[adjective], 'apple'].compact.join('-')
end  
    
apple_machine('beautiful')  #=> 'beautiful-apple'    
# 有return
def apple_machine(adjective)
  proc = Proc.new { |x| return x }
  
  [proc[adjective], 'apple'].compact.join('-')
end  
    
apple_machine('beautiful')  #=> 'beautiful'

Lambda

  • 使用 -> {}, lambda {}定义
  • 对於参数的引入有严格定义
proc = -> (x){ x }
#=> #<Proc:0x00007fd4de10a580@(irb):18 (lambda)>

proc.call(1)
#=> 1

proc.call
#=> ArgumentError (wrong number of arguments (given 0, expected 1))
  • lambda 里头写 return 就会继续往下走
# 有return
def apple_machine(adjective)
  proc = -> (x){ return x }
  
  [proc[adjective], 'apple'].compact.join('-')
end  
    
apple_machine('beautiful')  #=> 'beautiful-apple'  

Proc call

Day8 提过了基本的用法,这边直接从实例开始介绍。未来学会proc方法之後,若包成方法会导致增加程序码的易读性跟不必要性,或者只是懒得包方法的话,就可以使用呼叫Proc的方式,宣告一个匿名函式,在同一个方法里面使用。

若收到类别为阵列,将所有的元素前方加入井字号,并将阵列里的元素全部用-连起,并且在每一个元素前面+个#字号印出来。

displayed_record = -> (opcode) { "##{opcode}" }

at = -> (opcodes) do
  return displayed_record.call(opcodes) unless opcodes.is_a?(Array) 
  
  opcodes.map { |opcode| displayed_record.call(opcode) }.join('-')
end

# 使用方式

at.call(%w[50 51 54])             #=> "#50-#51-#54"
at.call('8000')                   #=> "#8000"

Proc.call sugar

Day8 已经提到Proc.call的四个语法糖,个人喜好的是第二种的.()的写法。汉汉老师内心总是想着,这种用法可以让没看过的人满头问号,若更偏激的软件工程师甚至可能把.改掉,这样一来可以跟看到有人改坏我的code了。

不过实际上这件事情并没有真正的发生过,以上都只是自己纯粹的想像跟脑补而已。

it_day10 = -> { puts "IT铁人赛第10天" }

it_day10.call
it_day10.()
it_day10[]
it_day10.===

What is & in Ruby

&与在Ruby的程序语言中,和C++的不太一样。Ruby& 可以将 proc(or lambda) 转为 block 使用

# 将 proc 转为 block
my_proc = Proc.new { |x| x + 2 }
[1, 2, 3].map(&my_proc)
=> [3, 4, 5]

# 将 proc 转为 block
[1, 2, 3].map(&->(x) { x + 2})
=> [3, 4, 5]

&也可以将 block 转为proc 使用

def it_day9(&it_block)
  it_block.call
end

# 直接使用 proc 
it_day9(&->{ 'foo' })
# 将 block 转为 proc 使用
it_day9 { 'foo' }

⭐️ & 搭配 symbol 会自动将symbol 转为 to_proc的用法。这边可能比较难理解,重点只要记住,&搭配符号,代表这个符号有proc的功能,因此可以to_proc,而这些可以to_proc的方法通常都是Ruby/ Rails 提供的方法。

# ruby 的 to_i, to_f, to_h 就是转型态的方法,同样的 to_proc 就是转为proc(or lambda)
:+.to_proc.call(1,1)         # 1+1
:upcase.to_proc.call('han')  # 'han'.upcase

# 上面的意思等同於下面的意思
[[1,1], [2,2], [3,3]].map(&:sum)
#=> [2, 4, 6]

[[1,1], [2,2], [3,3]].map {|arr| arr.sum}
#=> [2, 4, 6]

yield & Implicit Block

今天的另外一个主角yield

Day9 我们提到了所有跟yield的用法,如果只要会用法的话,看 Day9 就够了!今天会从Proc的角度来介绍blockblock专有名词称为Explicit Blocks,使用了&block name来做表示。

以下一共有两种方式可以将block带入使用

def it_day10(&it_block)
  it_block.call
end

# 一共有两种方式
it_day10(&->{ 'foo' })
it_day10 { 'foo' }

我们可以用yield 改写成下列的格式。yield表示法有个专有名词称为Implicit Blockyieldblock.call的简写,可以不引入参数,也可以引入参数。

# yield without argument
def it_day10
  yield
end

it_day10(&->{ 'foo' })
it_day10 { 'foo' }

然後,不能写return

# yield with argument
def it_day10_arg(x = 1)
  y = yield x
  
  y + 1
end

it_day10_arg(2, &->(x) { x + 1 }) #=> 4
it_day10_arg(2) { |x| x + 1 }     #=> 4

it_day10_arg(2) { |x| return x + 1 } #=> LocalJumpError (unexpected return)

yield 还可以搭配 block_given? 使用,可以区别block和没有block的使用方法

def it_day10
  block_given? ? yield : 'bar'
end

it_day10(&->{ 'foo' })  #=> 'foo'
it_day10                #=> 'bar'

结论

block差不多到这边就讲解完了,後续会介绍class 的用法。

参考资料


<<:  Material UI in React [Day9] Inputs (Radio) 单选 & (Switch) 开关

>>:  Day10 javascript for回圈

Day 27:碰到困难问题,演算法也救不了?

上一回我们说旅行推销员问题(TSP)是一个NP困难问题,没有快速的演算法可以解决。 那一个问题怎样叫...

[NestJS 带你飞!] DAY27 - Swagger (下)

API 操作设计 上一篇我们让 API 的参数能够顺利显示在 Swagger UI 中,在设计完参数...

Day1 : M365上的离职人员邮件要怎麽处理呢?

常会遇到这问题 , 离职人员的邮件要怎麽处理呢? 又不想花钱只为了留下资料,那要怎麽做呢? 可以选择...

Binary Search

二元搜寻BigO(log n) 相较於线性搜寻时间复杂度实在好太多 必须是被排序好的 由於每次对半砍...

[Day29] CSS display复习

display: block 区块元素介绍 特性 h1,ul,li,p 占满整个版面 可设定宽高 会...