初来乍到Ruby
世界的读者们,绝对想不到原来Ruby
也有 curry
, bind
等用法。这些语法对於JS
的使用者应该很熟悉,在今年的IT铁人赛就看到很多介绍bind
, curry
Closure
是程序语言中很基本的概念,首先要先讲Proc
和 closure
的关系
在Ruby
的Block则是用{}
或do ... end
来做领域的展开,领域里和领域外是完全不同的世界。外面的值没有办法干预到里面,而里头的数值和运算也不会影响到外界。这就是closure
的概念,我们称为闭包。
没有术式就不会有领域,而以 Ruby
来说,一共有下列几种术式能够展开领域。
#====== Proc
proc {}
Proc.new {}
#====== Proc(Lambda)
lambda {}
-> {}
#====== map, each...
[1, 2, 3].each {}
我们也可以自己做术式,而我们使用的术式为yield
。
block_given?
为yield
的好朋友,所以我们先介绍block_given?
def apple_machine
block_given? && 'block_used' || 'block_unused'
end
apple_machine #=> block_unused
apple_machine {} #=> block_used
由上面的例子我们可以知道, block_given?
可以侦测apple_machine
是否使用block
, block_given?
可以让我们在有block
或没有block
的情况下做应对,接着我们开始讲yield
。
# 情境1
def apple_machine
yield if block_given?
end
apple_machine { 1 } #=> 1
apple_machine { 1 + 1 } #=> 2
apple_machine { [1, 2, 3].sum } #=> 6
# 情境2
def washing_machine
yield if block_given?
2
end
apple_machine { 1 } #=> 2
apple_machine { 1 + 1 } #=> 2
apple_machine { [1, 2, 3].sum } #=> 2
yield
字面上的意思为让路。上述的情境1、情境2,想要传达给读者的讯息为不管是简单的1、稍微复杂的1+1,或者更复杂的[1, 2, 3].sum
。在进入block
以後,程序码的会先被Block
拿走主控权,结束block
以後再回归原来的程序。
# 情境3
def itday9_method(num = 0)
yield(num + 1) if block_given?
end
itday9_method #=> nil
itday9_method(2) { |n| n*100 } #=> 300
[1, 2, 3].map { |n| itday9_method(n) { |p| p*100 } } #=> [200, 300, 400]
我们可以利用yield
将方法里头的值传出去给外面的Block
,给外面的Block
计算以後再回传运算的结果回来。情境3的用法很重要
实际的状况下,我们传的不会只是num+1
,可以是绿界回传的付款结果、货运单状态、POS错误讯息等等。
yield
或许不是个好写法,因为使用太多的Block
会造成维护上的不易读性,不过个人在专案上用的地方还蛮多的。使用在只有自己在维护的专案里面,写了Block
就可以少想很多设计流程。
以下有几个例子,可以正大光明在Rails
使用Block
。
view
:如视窗画面、卡片、页签layout
:搭配content_for
,如使用Action Mailer
decorator pattern
,可以用yield
实现 ➡️ Day32 提到摊提Bind 其实是 React 使用 class component 比较常见的写法,但 bind 不是只能绑定 this
,还可以绑任何物件。下面的例子为,javascript
的bind
将里面的this
与外面的[1, 2, 3]
绑在一起。
function Point() {
return [...this, 4, 5, 6]
}
point = Point.bind([1, 2, 3])
point()
// [1, 2, 3, 4, 5, 6]
bind
是一种可以和外部世界联络的桥梁,而这个方法在Ruby
也有。
a = 123 # 外部变数
block = proc { a } # Procedure
block.binding.local_variable_get(:a) # 绑定外部的变数
block.call #=> 123
Curry
是Javascript
对function
的一种进阶用法,在JS
中也是汉汉老师常用的写法。此外,我也常常在面试中看到使用Curry
的题目
/* 公司後辈的面试题目 */
function adder(a) {
/* function 可以当作变数,因此我们可以将 function 回传出去。
意即为: function return 出来的结果还是 function */
return function(b) {
return a + b
}
}
/* 公司专案常见的写法,写法等同於上方 */
const adder = a => b => (a + b)
/* 范例1: a = 1, b = 2 */
adder(1)(2) // 3
/* 范例2: 用在map */
[1, 2, 3].map(e => adder(1)(e)) // [2, 3, 4]
范例1
用了两个括号把值回传出来,范例2为使用map
这个 Higher Order Function
,我们在使用Higher Order Function
时,里面的adder
必须也是function
。利用curry的特性,做一个匿名函数 e => adder(1)(e)
提供给map
使用。
curry
绝对不是炫技用,而是为了做到把function
可以变成变数的特性而衍生出的技巧。以下为汉汉老师实际上使用的curry
实例
/* 实际应用上使用的 curry (看不懂没关系) */
export const ajaxReload = (table) => () => table.api().ajax.reload();
export const multiAjaxReload = (tables) => () => tables.forEach(table => table.api().ajax.reload())
export const dataWithStatus = (selectedValue, type = null) => e => ({...e, [checkedOrSelected(type)]: is_in(e.value, selectedValue)})
Ruby
也有一样的方法,我们来看以下例子
divisible_by = ->(x,y) { (y % x).zero? }.curry
(1..10).select(&divisible_by.call(5)) #=> [5, 10]
(1..10).select(&divisible_by.call(2)) #=> [2, 4, 6, 8, 10]
我们分别传5, 2 给divisible_by
这个物件对,接着select
会使用divisible_by.call
作为筛选。至於 &
符号,我们会在Day10解释
Day10 我们会介绍&
,以及详细说明yield
的运作原理!
<<: 前端工程师也能开发全端网页:挑战 30 天用 React 加上 Firebase 打造社群网站|Day9 发表文章页面
>>: [13th-铁人赛]Day 4:Modern CSS 超详细新手攻略 - Display
本系列文之後也会置於个人网站 +----------+ | Resource | | Owner ...
前言 昨天我们套用了bootstrap4.6 今天来把登入画面也套上去 并且测试api吧 目标 新增...
前面提到了一些 Router 连网所需的设定,有 PPPoE、DHCP 以及 Static 这几种类...
大家好,我是YIYI,今天我要来制作记帐和报表的页面。 记帐页面 和制作前面的页面一样,先将BACK...
swift 接下来~就让我们还拉一下版面吧 设定背景 首先点选左侧 Main.storyboard ...