Proc 与 lambda

作者: 云莉6 | 来源:发表于2020-04-22 01:03 被阅读0次

本文截取一段 ruby doc 官网 Proc 的内容并翻译,试图更好的理解和总结 Proc(non-lambda) 和 lambda 的区别和使用场景。

Lambda and non-lambda semantics

Procs are coming in two flavors: lambda and non-lambda (regular procs). Differences are:

  • In lambdas, return means exit from this lambda;

  • In regular procs, return means exit from embracing method (and will throw LocalJumpError if invoked outside the method);

  • In lambdas, arguments are treated in the same way as in methods: strict, with ArgumentError for mismatching argument number, and no additional argument processing;

  • Regular procs accept arguments more generously: missing arguments are filled with nil, single Array arguments are deconstructed if the proc has multiple arguments, and there is no error raised on extra arguments.

p = proc {|x, y| "x=#{x}, y=#{y}" }
p.call(1, 2)      #=> "x=1, y=2"
p.call([1, 2])    #=> "x=1, y=2", array deconstructed
p.call(1, 2, 8)   #=> "x=1, y=2", extra argument discarded
p.call(1)         #=> "x=1, y=", nil substituted instead of error

l = lambda {|x, y| "x=#{x}, y=#{y}" }
l.call(1, 2)      #=> "x=1, y=2"
l.call([1, 2])    # ArgumentError: wrong number of arguments (given 1, expected 2)
l.call(1, 2, 8)   # ArgumentError: wrong number of arguments (given 3, expected 2)
l.call(1)         # ArgumentError: wrong number of arguments (given 1, expected 2)

def test_return
  -> { return 3 }.call      # just returns from lambda into method body
  proc { return 4 }.call    # returns from method
  return 5
end

test_return # => 4, return from proc

直接翻译:

Procs 有两种形式:lambdanon-lambda (regular procs)。它们的不同之处在于:

  • lambda 中,return 表示从该 lambda 退出;
  • 常规 proc 中,return 表示从该方法退出(如果在方法外部调用,将抛 LocalJumpError);
  • lambda 中,对参数的处理方式与方法相同:strict(严格),使用 ArgumentError 表示参数编号不匹配,并且不对附加参数进行处理;
  • 常规 proc 更慷慨地接受参数:缺少的参数填充为 nil,如果 proc 具有多个参数,则解构单个 Array 参数,并且在附加参数上不引发错误。

个人更白话的理解:

常规(非lambda)proc lambda
return 退出整个 method return 仅从 lambda 内部退出
慷慨地接受参数:缺少的参数填充为 nil,如果 proc 具有多个参数,则解构单个 Array 参数,并且在附加参数上不引发错误 strict(严格),使用 ArgumentError 表示参数编号不匹配,并且不对附加参数进行处理

Lambdas are useful as self-sufficient functions, in particular useful as arguments to higher->order functions, behaving exactly like Ruby methods.

直接翻译

Lambda 作为自给自足的函数很有用,特别是作为高阶函数的参数时,与Ruby方法完全一样。ps: 这句话可以帮助牢记,lambda 的检查参数和 return lambda 自身函数的性质。

Procs are useful for implementing iterators:

def test
  [[1, 2], [3, 4], [5, 6]].map {|a, b| return a if a + b > 10 }
                            #  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
end

Inside map, the block of code is treated as a regular (non-lambda) proc, which means that the internal arrays will be deconstructed to pairs of arguments, and return will exit from the method test. That would not be possible with a stricter lambda.

代码执行结果:

2.4.4 :005 > def test
2.4.4 :006?>     [[1, 2], [3, 4], [5, 6]].map {|a, b| return a if a + b > 10 }
2.4.4 :007?>                               #  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2.4.4 :008?>   end
 => :test
2.4.4 :009 > test
 => 5

直接翻译

Procs 在迭代中很有用,在 map 内部,代码块被视为常规(非lambda)proc,这意味着内部数组将被解构为成对的参数,而 return 将退出方法 test。如果使用更严格的 lambda,将不可能做到。

个人白话理解

Procs 这种常规(非lambda)proc 方式,在 map 这种迭代器中是很好的,上图看执行结果,可以实现在 map 内部直接 return 整个 test 方法。这比使用 lambda 更符合我们的代码意图。

The only exception is dynamic method definition: even if defined by passing a non-lambda proc, methods still have normal semantics of argument checking.

class C
 define_method(:e, &proc {})
end

C.new.e(1,2)       #=> ArgumentError
C.new.method(:e).to_proc.lambda?   #=> true

This exception ensures that methods never have unusual argument passing conventions, and makes it easy to have wrappers defining methods that behave as usual.

class C
 def self.def2(name, &body)
   define_method(name, &body)
 end

def2(:f) {}
end
C.new.f(1,2)       #=> ArgumentError

The wrapper def2 receives body as a non-lambda proc, yet defines a method which has normal semantics.

直接翻译

唯一的例外是动态方法定义:即使通过传递 非lambda proc 进行定义,方法仍然具有参数检查的常规语义。此异常可确保方法永远不会具有异常的参数传递约定,并使包装器轻松定义照常运行的方法。包装器 def2 将主体作为 非lambda proc 接收,但定义了一种具有正常语义的方法。

个人白话理解

例外的是,即使通过非 lambda 的 proc 去动态定义 method,也会按照 lambda 的方式严格检查参数,这保证了动态定义方法的正常运行,不会出现和正常的 method 不同的行为。

另外我也从 Rails 的 scope 为什么用 lambda?Proc 与 lambda 有什么不同之处? 这篇博文得到了些启发,我在记住这两种 Procs 不同点的时候,时常忘记到底是 常规 proc 检查参数还是 lambda,但如果能从「Rails 的 scope 为什么用 lambda?」这个问题来思考的话,就更加的能记忆清楚了。Railsscope 之所以用 lambda 就是因为 lambda 会严格检查参数可以避免 Proc 对于「缺少的参数填充为 nil」的行为导致 sql 查询有误。

总结

由上面分析可以看出,Proc(non-lambda)lambda 都有各自的用武之处,通过对代码的预期不同,来灵活使用从而达到准确实作功能的意图。从这点上来讲,深入了解两种 Procs 确实有助于我们写出更合适的代码并理解代码本身。

相关文章

  • Proc 与 lambda

    本文截取一段 ruby doc 官网 Proc 的内容并翻译,试图更好的理解和总结 Proc(non-lambd...

  • ruby proc/lambda/method/block

    一:proc / lambda 1.让我们分别以proc和lambda两种方式来定义代码块 2.proc和lamb...

  • 如何进行代码块打包 && proc vs la

    proc和lambda的对比: proc和lambda都是代码块打包的工具不同之处在于:proc对参数的自适应性,...

  • ruby Method proc lambda 和闭包

    Proc和Lambda的区别,主要是: Proc和Lambda都是对象,而Block不是 参数列表中最多只能有一个...

  • Proc、proc、lambda、yield self

    创建Proc对象 Proc.new proc lambda -> &,将代码块作为非匿名参数传递 &操作符的含义 ...

  • Ruby中Proc,Lambda

    Ruby中的 Proc 和 Lambda 有些类似 high-order function。Proc 和 Lamb...

  • block, proc, lambda

    &:to_i是一个block,block不能独立存在,同时你也没有办法直接存储或传递它,必须把block挂在某个方...

  • Block、Proc 与 Lambda的区别

    Ruby的Block块是它的关键特色之一,用块能够写出简明且高度可重用的算法。我们也会经常用到,例如:produc...

  • Ruby block | lambda | Proc | yie

    据说每个面试ruby的公司都会问这个问题。 block 依旧是从代码直接开始 上面两个用的是Array的sort函...

  • ruby 方法调用中的*和&

    在ruby方法定义、调用中,*可以把数组转化为参数,&可以把Proc或lambda转化为块,在开发中可以起到很好的...

网友评论

    本文标题:Proc 与 lambda

    本文链接:https://www.haomeiwen.com/subject/rdimihtx.html