Ruby中的block、proc、lambda区别总结

在规则引擎中,Ruby 的闭包使用特别频繁,而且有 block,Proc和 lambda 等后几种形式的用法,很让人困惑。为了深入理解代码,再次认真学习了一下 Ruby 的闭包,特别是 block,proc 和 lambda 几种用法的异同,这次的周记就和大家分享一下心得。

闭包是 Ruby 相对其它语言特别优势之一,很多语言有闭包,但是唯有 Ruby 把闭包的优势发挥得淋漓尽致。Ruby 的哲学之一:同一个问题现实中有多种解决方法,所以 Ruby 中也有多种解法,所以闭包的使用也有多种方法。

先看一个代码的例子:

Example 1:

代码如下:

def foo1
  yield
end

def foo2(&b)
  b.call if b
end

foo1 { puts "foo1 in block" }
foo2 { puts "foo2 in block" }
proc = Proc.new { puts "foo in proc" }
foo1(&proc)
foo2(&proc)
lambda_proc = lambda { puts "foo in lambda" }
foo1(&lambda_proc)
foo2(&lambda_proc)

输出:

代码如下:

》foo1 in block
》foo2 in block
》foo in proc
》foo in proc
》foo in lambda
》foo in lambda

大家是不是有些困惑,首先是方法 foo1 和 foo2 都能接收闭包,它们这两种写法有什么区别,然后是作为参数的三种闭包 block,proc和 lambda有什么区别。

1. yield 和 block call 的区别

yield 和 block call 两种都能实现运行闭包,从实际运行效果来说,区别不大。其区别主要在于:

1.1 闭包的传递和保存

因为闭包已经传递到参数里,所以可以继续传递或保存起来,例如:

Exampl 2:

代码如下:

class A
      def foo1(&b)
         @proc = b
      end
      def foo2
          @proc.call if @proc
      end
   end

a = A.new
    a.foo1 { puts "proc from foo1" }
    a.foo2

1.2 性能

yield不是方法调用,而是 Ruby 的关键字,yield 要比 block call 比快 1 倍左右。

2. block 和 proc, lambda 的区别

很简单直接,引入 proc 和 lambda 就是为了复用,避免重复定义,例如在 example 1 中,使用 proc 变量存储闭包,避免重复定义两个 block 。

3. proc 和 lambda 的区别

这大概是最让人困惑的地方,从 Example 1 的行为上看,他们的效果是一致的,为什么要用两种不同的表达方式。

代码如下:

proc = Proc.new { puts "foo in proc" }
   lambda_proc = lambda { puts "foo in lambda" }

确实,对于简单的情况,比如 Example 1的情况,他们的行为是一致的,但是主要在两个地方有明显差异:

1.1 参数检查

还是例子说话 Example 3:

代码如下:

def foo
      x = 100
      yield x
   end

proc = Proc.new { |a, b| puts "a is #{a.inspect} b is #{b.inspect}" }
   foo(&proc)

lambda_proc1 = lambda { |a| puts "a is #{a.inspect}" }
   foo(&lambda_proc1)
   lambda_proc2 = lambda { |a, b| puts "a is #{a.inspect} b is #{b.inspect}" }
   foo(&lambda_proc2)

输出

代码如下:

》a is 100 b is nil
   》a is 100
   》ArgumentError: wrong number of arguments (1 for 2)
      …

可见,proc 不会对参数进行个数匹配检查,而 lambda 会,如果不匹配还会报异常,所以安全起见,建议优先用 lambda。

1.2 返回上层

还是例子说话 Example 4:

代码如下:

def foo
     f = Proc.new { return "return from foo from inside proc" }
     f.call # control leaves foo here
     return "return from foo"
   end

def bar
     f = lambda { return "return from lambda" }
     puts f.call # control does not leave bar here
     return "return from bar"
   end

puts foo
   puts bar

输出

代码如下:

》return from foo from inside proc
   》return from lambda
   》return from bar

可见,proc 中,return 相当于执行了闭包环境里的 return,而 lambda 只是返回call 闭包所在环境。

总结:闭包是 Ruby 的强大特性,它的几种表达方式block,proc 和 lambda有细微差别,要用好它需要对其深入理解。

(0)

相关推荐

  • C++中的Lambda表达式详解

    我是搞C++的 一直都在提醒自己,我是搞C++的:但是当C++11出来这么长时间了,我却没有跟着队伍走,发现很对不起自己的身份,也还好,发现自己也有段时间没有写C++代码了.今天看到了C++中的Lambda表达式,虽然用过C#的,但是C++的,一直没有用,也不知道怎么用,就可怜的连Lambda语法都看不懂.好了,这里就对C++中的Lambda进行一个简单的总结,就算是对自己的一个交代,我是搞C++的,我是一个C++ programmer. 一段简单的Code 我也不是文艺的人,对于Lambda的

  • 浅析C++11新特性的Lambda表达式

    lambda简介 熟悉Python的程序员应该对lambda不陌生.简单来说,lambda就是一个匿名的可调用代码块.在C++11新标准中,lambda具有如下格式: [capture list] (parameter list) -> return type { function body } 可以看到,他有四个组成部分: 1.capture list: 捕获列表 2.parameter list: 参数列表 3.return type: 返回类型 4.function body: 执行代码

  • python中lambda函数 list comprehension 和 zip函数使用指南

    lambda 函数 Python 支持一种有趣的语法,它允许你快速定义单行的最小函数.这些叫做 lambda 的函数,是从 Lisp 借用来的,可以用在任何需要函数的地方. def f(x): return x*2,用lambda函数来替换可以写成:g = lambda x: x*2`g(3)结果是6.(lambda x: x*2)(3)`也是同样的效果. 这是一个 lambda 函数,完成同上面普通函数相同的事情.注意这里的简短的语法:在参数列表周围没有括号,而且忽略了 return 关键字

  • 结合C++11新特性来学习C++中lambda表达式的用法

    在 C++ 11 中,lambda 表达式(通常称为 "lambda")是一种在被调用的位置或作为参数传递给函数的位置定义匿名函数对象的简便方法. Lambda 通常用于封装传递给算法或异步方法的少量代码行. 本文定义了 lambda 是什么,将 lambda 与其他编程技术进行比较,描述其优点,并提供一个基本示例. Lambda 表达式的各部分 ISO C++ 标准展示了作为第三个参数传递给 std::sort() 函数的简单 lambda: #include <algorit

  • Ruby中使用Block、Proc、lambda实现闭包

    闭包(Closure),是指未绑定到任何对象的自由代码,闭包中的代码与任何对象和全局变量无关,只与执行此段代码的上下文相关. 今天我们简要的看一下ruby中的闭包实现. Ruby中的闭包实现有:Block,Proc,Lambada. 首先,我们来看Block. 复制代码 代码如下: ary = [1,2,3,4] ary.collect! do |a| a*a end ary.each do |a| puts a end 这段代码,我们使用了Array对象的block方法,将ary中的每个元素平

  • Python中的特殊语法:filter、map、reduce、lambda介绍

    filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)返回: 复制代码 代码如下: >>> def f(x): return x % 2 != 0 and x % 3 != 0 >>> filter(f, range(2, 25)) [5, 7, 11, 13, 17, 19, 23] >>

  • C++实现的一个可以写递归lambda的Y函数

    最近学习C++11的variadic template argument,终于可以摆脱用fpmacro模板来复制一大堆代码的做法了,好开心.这个例子的main函数用lambda写了一个斐波那契数列的递归计算函数.跟以往不同的是,在Y函数的帮助下,这个lambda表达是可以成功看到自己,然后递归调用.当然这仍然需要用普通的C++递归来实现,并不是λ-calculus那个高大上的Y Combinator. #include <functional> #include <memory>

  • 实例讲解C++编程中lambda表达式的使用

    函数对象与Lambdas 你编写代码时,尤其是使用 STL 算法时,可能会使用函数指针和函数对象来解决问题和执行计算.函数指针和函数对象各有利弊.例如,函数指针具有最低的语法开销,但不保持范围内的状态,函数对象可保持状态,但需要类定义的语法开销. lambda 结合了函数指针和函数对象的优点并避免其缺点.lambda 与函数对象相似的是灵活并且可以保持状态,但不同的是其简洁的语法不需要显式类定义. 使用lambda,相比等效的函数对象代码,您可以写出不太复杂并且不容易出错的代码. 下面的示例比较

  • Ruby中proc和lambda的两个区别

    1.在proc和lambda中,return关键字有不同含义: 在proc中,return仅仅表示从这个lambda中返回. 在lambda中,return不是从proc中返回,而是从定义proc的作用域中返回. 复制代码 代码如下: def one_method     p = Proc.new{return 10}  #调用这个块的时候,从p的作用域直接返回10,因此下面的return将不会执行     result = p.call     return result * 2 end  

  • Ruby中的block、proc、lambda区别总结

    在规则引擎中,Ruby 的闭包使用特别频繁,而且有 block,Proc和 lambda 等后几种形式的用法,很让人困惑.为了深入理解代码,再次认真学习了一下 Ruby 的闭包,特别是 block,proc 和 lambda 几种用法的异同,这次的周记就和大家分享一下心得. 闭包是 Ruby 相对其它语言特别优势之一,很多语言有闭包,但是唯有 Ruby 把闭包的优势发挥得淋漓尽致.Ruby 的哲学之一:同一个问题现实中有多种解决方法,所以 Ruby 中也有多种解法,所以闭包的使用也有多种方法.

  • 深入理解Ruby中的block概念

    Ruby 里的 block一般翻译成代码块,block 刚开始看上去有点奇怪,因为很多语言里面没有这样的东西.事实上它还不错. First-class function and Higher-order function First-class function 和 Higher-order function 是函数式编程语言里面的概念,听起来好像很高端的样子,其实很很简单的. First-class functions 是指在某些语言里,函数是一等公民,可以把函数当做参数传递, 可以返回一个函

  • Ruby中的block代码块学习教程

    1.什么是代码块 在Ruby中,{}或do...end之间的代码是一个代码块.代码块只能出现在一个方法的后边,它紧接在方法最后一个参数的同一行上,由yield关键字调用.例如: [1,2,3,4,5].each { |i| puts i } [1,2,3,4,5].each do |i| puts i end 块变量:以yield关键字调用block也可以传递参数,block中竖线(|)之间给出的参数名用于接收来自yield的参数. 竖线之间(如上例中的 | i |)的变量被称作块变量,作用和一

  • 深入理解Ruby中的代码块block特性

    block是什么? 在Ruby中,block并不罕见.官方对block的定义是"一段被包裹着的代码".当然,我觉得这样的解释不会让你变的更明白. 对block的一种更简单的描述是"一个block就是一段存储在一个变量中的代码,它和其他的对象一样,可以被随时的运行" 然后,咱们通过看一些代码,之后再把这些代码重构成Ruby中的block形式.通过代码来实际的感受,更加直观. 比如,对两个数做加法? puts 5 + 6 # => 11 嗯,这样写是可以的.但是,

  • Ruby中的public、private、protected区别小结

    重点关注private与protected public 默认即为public,全局都可以访问,这个不解释 private C++, "private" 意为 "private to this class", 但是Ruby中意为 "private to this instance". 意思是:C++中,对于类A,只要能访问类A,就能访问A的对象的private方法. Ruby中,却不行:你只能在你本对象的实例中访问本对象的private方法. 因

  • Ruby中Block和迭代器的使用讲解

    我们来简单地描述Ruby的一个独特特性.Block,一种可以和方法调用相关联的代码块,几乎就像参数一样.这是一个不可思议的功能强大的特性. 可以用Block实现回调(但它比Java的匿名内部(anonymous inner)类更简单),传递一组代码(但它远比c的函数指针灵活),以及实现迭代器. Block只是在花括号或者do...end之间的一组代码. {puts "Hello"} #this is a block do ### club.enroll(person) #and so

  • Ruby中的p和puts的使用区别浅析

    p 和 puts 是 Ruby 中特别常用的方法,很多童鞋可能认为它们是差不多的,使用的时候也不加注意,但是仔细考究起来,它们是有明显差别的. 先举一个例子 复制代码 代码如下: class Foo     def inspect       "foo from inspect"     end def to_s       "foo from to_s"     end   end foo = Foo.new   p foo   puts foo   p &quo

  • Ruby中的return、break、next详解

    return,break,next 这几个关键字的使用都涉及到跳出作用域的问题,而他们的不同 则在于不同的关键字跳出去的目的作用域的不同,因为有代码块则导致有一些地方需要格外注意. return 常用方式 通常情况下的return语句和大家理解的意思是相同的. 复制代码 代码如下: def m1 param   if param == 1     return 'returned 1'   end 'returned default value'#根据Ruby语言规范,最后一条执行语句的结果将作

  • 详解Ruby中的代码块及其参数传递

    一,块的声明    块的声明在函数调用之后,用{..}括起来,或do..end封装.{}一般用在单行语句上,do..end用在多行语句上. (1..4).each{|v| print "#{v} "} #输出1 2 3 4 块可以带参数,与函数参数不同,块参数用||封装,当然,可以带多个参数.这些参数怎么定义,实际上是在函数内部定义好的,后面会讲到. 二,块内变量的访问    块内可以访问块外的变量,也就是块外的变量在块内是可见的,如 sum = 0 (1..5).each do |v

随机推荐