Ruby的代码块是否与C#的lambda表达式相同? [英] Is Ruby's code block same as C#'s lambda expression?

查看:46
本文介绍了Ruby的代码块是否与C#的lambda表达式相同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这两个基本上是同一回事吗?他们看起来和我非常相似.

Are these two essentially the same thing? They look very similar to me.

lambda表达式是否借鉴了Ruby的想法?

Did lambda expression borrow its idea from Ruby?

推荐答案

Ruby实际上有4个极其相似的构造

Ruby actually has 4 constructs that are all extremely similar

区块背后的想法是一种实现非常轻量级策略模式的方式.块将在函数上定义一个协程,该函数可以使用yield关键字将控制委派给该协程.我们几乎在ruby中的所有地方都使用了块,包括几乎所有的循环结构或在c#中使用 using 的任何地方.块之外的任何内容都在该块的作用域内,但是反之则不成立,除了在该块内返回将返回外部作用域.他们看起来像这样

The idea behind blocks is sort of a way to implement really light weight strategy patterns. A block will define a coroutine on the function, which the function can delegate control to with the yield keyword. We use blocks for just about everything in ruby, including pretty much all the looping constructs or anywhere you would use using in c#. Anything outside the block is in scope for the block, however the inverse is not true, with the exception that return inside the block will return the outer scope. They look like this

def foo
  yield 'called foo'
end

#usage
foo {|msg| puts msg} #idiomatic for one liners

foo do |msg| #idiomatic for multiline blocks
  puts msg
end

Proc

proc基本上是在获取一个块并将其作为参数传递.一个非常有趣的用法是,您可以在另一个方法中传递proc来代替块.Ruby的proc强制具有特殊字符& ;,并且有一个特殊规则,即如果方法签名中的最后一个参数以&开始,它将是该方法调用块的proc表示.最后,有一个名为 block_given?的内置方法,如果当前方法已定义一个块,则该方法将返回true.看起来像这样

Proc

A proc is basically taking a block and passing it around as a parameter. One extremely interesting use of this is that you can pass a proc in as a replacement for a block in another method. Ruby has a special character for proc coercion which is &, and a special rule that if the last param in a method signature starts with an &, it will be a proc representation of the block for the method call. Finally, there is a builtin method called block_given?, which will return true if the current method has a block defined. It looks like this

def foo(&block)
  return block
end

b = foo {puts 'hi'}
b.call # hi

要对此进行更深入的介绍,rails被添加到Symbol中(并在1.9中被合并为核心红宝石),这是一个非常巧妙的技巧.基本上,强制通过在其旁边的任何地方调用 to_proc 来实现其魔力.因此,Rails伙计们添加了一个Symbol#to_proc,它将在传入的任何内容中进行调用.这使您可以为仅在列表中每个对象上调用方法的任何聚合样式函数编写一些真正简洁的代码

To go a little deeper with this, there is a really neat trick that rails added to Symbol (and got merged into core ruby in 1.9). Basically, that & coercion does its magic by calling to_proc on whatever it is next to. So the rails guys added a Symbol#to_proc that would call itself on whatever is passed in. That lets you write some really terse code for any aggregation style function that is just calling a method on every object in a list

class Foo
  def bar
    'this is from bar'
  end
end

list = [Foo.new, Foo.new, Foo.new]

list.map {|foo| foo.bar} # returns ['this is from bar', 'this is from bar', 'this is from bar']
list.map &:bar # returns _exactly_ the same thing

更多高级内容,但imo确实说明了您可以使用proc进行的魔术操作

More advanced stuff, but imo that really illustrates the sort of magic you can do with procs

lambda的用途在ruby中与在c#中几乎相同,这是一种创建内联函数以传递或在内部使用的方法.像块和proc一样,lambda是闭包,但与前两个不同,它强制执行arity,并且从lambda返回将退出lambda,而不是包含作用域.您可以通过将一个块传递给lambda方法或在ruby 1.9中传递给->来创建一个

The purpose of a lambda is pretty much the same in ruby as it is in c#, a way to create an inline function to either pass around, or use internally. Like blocks and procs, lambdas are closures, but unlike the first two it enforces arity, and return from a lambda exits the lambda, not the containing scope. You create one by passing a block to the lambda method, or to -> in ruby 1.9

l = lambda {|msg| puts msg} #ruby 1.8
l = -> {|msg| puts msg} #ruby 1.9

l.call('foo') # => foo

方法

只有认真的红宝石爱好者才真正理解这一点:)方法是一种将现有函数转换为可以放入变量的方法.您可以通过调用 method 函数并传入一个符号作为方法名称来获得方法.您可以重新绑定方法,或者如果您想炫耀,也可以将其强制为proc.一种重写先前方法的方法是

Methods

Only serious ruby geeks really understand this one :) A method is a way to turn an existing function into something you can put in a variable. You get a method by calling the method function, and passing in a symbol as the method name. You can re bind a method, or you can coerce it into a proc if you want to show off. A way to re-write the previous method would be

l = lambda &method(:puts)
l.call('foo')

这里发生的事情是,您正在创建一个put方法,将其强制为proc,然后将其作为lambda方法的块的替代方法传递,这反过来又会返回lambda方法

What is happening here is that you are creating a method for puts, coercing it into a proc, passing that in as a replacement for a block for the lambda method, which in turn returns you the lambda

请随时提出任何不清楚的问题(在一个没有IRB的工作日的深夜里写这本书,希望那不是纯粹的胡言乱语)

Feel free to ask about anything that isn't clear (writing this really late on a weeknight without an irb, hopefully it isn't pure gibberish)

解决评论中的问题

list.map&:bar我可以使用这种语法吗一个代码块需要花费超过一个论点?假设我有哈希= {0 =>"hello",1 =>"world"},我想选择具有0的元素作为钥匙.也许不是一个很好的例子.–布赖恩沉

list.map &:bar Can I use this syntax with a code block that takes more than one argument? Say I have hash = { 0 => "hello", 1 => "world" }, and I want to select the elements that has 0 as the key. Maybe not a good example. – Bryan Shen

这里会更深入,但是要真正了解它是如何工作的,您需要了解ruby方法调用是如何工作的.

Gonna go kind of deep here, but to really understand how it works you need to understand how ruby method calls work.

基本上,ruby没有调用方法的概念,发生的是对象之间相互传递消息.您使用的 obj.method arg 语法实际上只是更明确形式的糖,即 obj.send:method,arg ,在功能上等效于第一种语法.这是语言的基本概念,这就是为什么 method_missing respond_to?之类的东西有意义的原因,在第一种情况下,您只是处理无法识别的消息,第二种情况是正在检查它是否正在侦听该消息.

Basically, ruby doesn't have a concept of invoking a method, what happens is that objects pass messages to each other. The obj.method arg syntax you use is really just sugar around the more explicit form, which is obj.send :method, arg, and is functionally equivalent to the first syntax. This is a fundamental concept in the language, and is why things like method_missing and respond_to? make sense, in the first case you are just handling an unrecognized message, the second you are checking to see if it is listening for that message.

要知道的另一件事是比较深奥的"splat"运算符, * .根据使用位置的不同,它实际上会做很多不同的事情.

The other thing to know is the rather esoteric "splat" operator, *. Depending on where its used, it actually does very different things.

def foo(bar, *baz)

在方法调用中,如果它是最后一个参数,则splat将使该参数遍历传递给函数的所有其他参数(类似于C#中的 params )

In a method call, if it is the last parameter, splat will make that parameter glob up all additional parameters passed in to the function (sort of like params in C#)

obj.foo(bar, *[biz, baz])

在方法调用(或其他任何带有参数列表的方法)中,它将把数组变成裸参数列表.下面的代码段与上面的代码段等效.

When in a method call (or anything else that takes argument lists), it will turn an array into a bare argument list. The snippet below is equivilent to the snippet above.

obj.foo(bar, biz, baz)

现在,考虑到 send * ,基本上这样实现 Symbol#to_proc

Now, with send and * in mind, Symbol#to_proc is basically implemented like this

class Symbol
  def to_proc
    Proc.new { |obj, *args| obj.send(self, *args) }
  end
end

因此,& :: sym 将要创建一个新的proc,它在传递给它的第一个参数上调用 .send:sym .如果传递了其他任何arg,则将它们组合到名为 args 的数组中,然后将其喷溅到 send 方法调用中.

So, &:sym is going to make a new proc, that calls .send :sym on the first argument passed to it. If any additional args are passed, they are globbed up into an array called args, and then splatted into the send method call.

我注意到&用于三位置:def foo(& block),list.map&:bar,而l = lambda& method(:puts).它们有相同的意思吗?–布莱恩·申(Bryan Shen)

I notice that & is used in three places: def foo(&block), list.map &:bar, and l = lambda &method(:puts). Do they share the same meaning? – Bryan Shen

是的,他们这样做.&将会调用 to_proc 旁边的所有内容.对于方法定义,它在最后一个参数上具有特殊含义,在该参数上,您将定义为块的协同例程拉入,并将其转换为proc.方法定义实际上是语言中最复杂的部分之一,在参数以及参数的位置中可能有大量的技巧和特殊含义.

Yes, they do. An & will call to_proc on what ever it is beside. In the case of the method definition it has a special meaning when on the last parameter, where you are pulling in the co-routine defined as a block, and turning that into a proc. Method definitions are actually one of the most complex parts of the language, there are a huge amount of tricks and special meanings that can be in the parameters, and the placement of the parameters.

b = {0 =>"df",1 =>"kl"} p b.select{|键,值|key.zero?} 我尝试过了将其转换为p b.select&:zero ?,但是失败了.我想那是因为代码的参数数块是两个,但& ;:零?只可以拿一个参数.有什么办法可以去做?–布莱恩·申

b = {0 => "df", 1 => "kl"} p b.select {|key, value| key.zero? } I tried to transform this to p b.select &:zero?, but it failed. I guess that's because the number of parameters for the code block is two, but &:zero? can only take one param. Is there any way I can do that? – Bryan Shen

应该更早解决此问题,不幸的是,您无法使用此技巧来实现.

This should be addressed earlier, unfortunately you can't do it with this trick.

一种方法是一种改变现有方法的方法功能可以放入的东西一个变量."为什么是l = method(:puts)还不够?lambda&是什么在这种情况下是什么意思?–布莱恩·申

"A method is a way to turn an existing function into something you can put in a variable." why is l = method(:puts) not sufficient? What does lambda & mean in this context? – Bryan Shen

该示例非常特殊,我只是想显示与之前示例等效的代码,在该示例中,我将proc传递给 lambda 方法.我将花一些时间重新编写该位,但是您是正确的, method(:puts)完全足够.我试图说明的是,您可以在任何可能会阻塞的地方使用& method(:puts).一个更好的例子就是这个

That example was exceptionally contrived, I just wanted to show equivalent code to the example before it, where I was passing a proc to the lambda method. I will take some time later and re-write that bit, but you are correct, method(:puts) is totally sufficient. What I was trying to show is that you can use &method(:puts) anywhere that would take a block. A better example would be this

['hello', 'world'].each &method(:puts) # => hello\nworld

l =-> {| msg |放入味精} #ruby 1.9:这对我不起作用.我之后检查了约尔格的答案,我认为应该是l =->(msg){puts msg}.或者也许我使用的版本不正确红宝石?我的是红宝石1.9.1p738 –布莱恩·申(Bryan Shen)

l = -> {|msg| puts msg} #ruby 1.9: this doesn't work for me. After I checked Jörg's answer, I think it should be l = -> (msg) {puts msg}. Or maybe i'm using an incorrect version of Ruby? Mine is ruby 1.9.1p738 – Bryan Shen

就像我在帖子中所说的那样,我在写答案时没有可用的irb,而您是对的,我很讨厌(将我的大部分时间都花在1.8.7上,所以我不是习惯了新语法)

Like I said in the post, I didn't have an irb available when I was writing the answer, and you are right, I goofed that (spend the vast majority of my time in 1.8.7, so I am not used to the new syntax yet)

钉子和孔之间没有空间.尝试 l =->(msg){放入msg} .实际上,这种语法有很多抵触之处,因为它与语言中的所有其他语法都大不相同.

There is no space between the stabby bit and the parens. Try l = ->(msg) {puts msg}. There was actually a lot of resistance to this syntax, since it is so different from everything else in the language.

这篇关于Ruby的代码块是否与C#的lambda表达式相同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆