- 浏览: 202905 次
- 来自: ...
文章分类
最新评论
-
赤道螞蟻:
如果是數據庫有定時任務,定時更新表的數據。 表中數據變化時,主 ...
用socket.io实现WebSocket的一个简单例子 -
cwalet:
在世界的中心呼喚愛 写道提示找不到 expressnpm in ...
用socket.io实现WebSocket的一个简单例子 -
在世界的中心呼喚愛:
提示找不到 express
用socket.io实现WebSocket的一个简单例子 -
Anleb:
def m1(a)
puts 'invoke m1'
pu ...
Ruby的一些疑问 -
biyeah:
补充,任何类,只要实现to_proc方法,都可以与&结 ...
Ruby的一些疑问
原文:
感谢作者的辛勤劳动。
- http://kenbeit.com/posts/87/ruby%E4%B8%AD%E7%9A%84%E9%97%AD%E5%8C%85
感谢作者的辛勤劳动。
# CLOSURES IN RUBY Paul Cantrell http://innig.net # Email: username "cantrell", domain name "pobox.com" # # 翻译: kenshin54 http://kenbeit.com # I recommend executing this file, then reading it alongside its output. # 我强烈建议执行此脚本,然后根据它的输出来理解它。 # # Alteratively, you can give yourself a sort of Ruby test by deleting all the comments, # then trying to guess the output of the code! # 或者,你可以给自己做一个ruby测试,删除所有的注释,然后猜测代码输出的结果 # A closure is a block of code which meets three criteria: # 闭包是一个满足3个标准的代码块: # # * It can be passed around as a value and # * 它可以作为一个值被传入 # # * executed on demand by anyone who has that value, at which time # * 在任何时候根据不同人的需要来执行它 # # * it can refer to variables from the context in which it was created # (i.e. it is closed with respect to variable access, in the # mathematical sense of the word "closed"). # * 它可以使用创建它的那个上下文中的变量(即它是对封闭变量的访问,数学意义上的词“封闭”) # # (The word "closure" actually has an imprecise meaning, and some people don't # think that criterion #1 is part of the definition. I think it is.) # (词“闭包”实际上有一个不明确的意义,有一些人不认为标准#1是它定义的一部分,但我认为它是。) # # Closures are a mainstay of functional languages, but are present in many other # languages as well (e.g. Java's anonymous inner classes). You can do cool stuff # with them: they allow deferred execution, and some elegant tricks of style. # 闭包是一个函数式语言的主体,但是也存在与其他很多语言中(比如Java中的匿名内部类)。 # 你可以用它来做一些很酷的东西:他们可以延后执行,和一些优雅的技巧。 # # Ruby is based on the "principle of least surprise," but I had a really nasty # surprise in my learning process. When I understood what methods like "each" # were doing, I thought, "Aha! Ruby has closures!" But then I found out that a # function can't accept multiple blocks -- violating the principle that closures # can be passed around freely as values. # Ruby基于“最小惊讶原则”,但是在我的学习过程中却是感到了惊讶。当我理解像“each” # 这样的方法在做什么时,我想,“啊,Ruby有闭包!”。但是我发现一个函数无法接收 # 多个块——违反闭包可以被任意的传递值的原则。 # # This document details what I learned in my quest to figure out what the deal is # 这个文档详述了在我弄清闭包的处理时我所学到的东西。 def example(num) puts puts "------ Example #{num} ------" end # ---------------------------- Section 1: Blocks ---------------------------- # -------------------------------- 章节一:块 ------------------------------- # Blocks are like closures, because they can refer to variables from their defining context: # 块就像闭包,因为他们可以使用定义它们的那个上下文中的变量。 example 1 def thrice yield yield yield end x = 5 puts "value of x before: #{x}" thrice { x += 1 } puts "value of x after: #{x}" # A block refers to variables in the context it was defined, not the context in which it is called: # 一个块使用定义它的上下文中的变量,而不是被调用的上下文中的变量。 example 2 def thrice_with_local_x x = 100 yield yield yield puts "value of x at end of thrice_with_local_x: #{x}" end x = 5 thrice_with_local_x { x += 1 } puts "value of outer x after: #{x}" # A block only refers to *existing* variables in the outer context; if they don't exist in the outer, a # block won't create them there: # 一个块仅仅使用定义它的上下文中已经存在的变量,如果变量不存在外于部上下文,块不会去创建他们。 example 3 thrice do # note that {...} and do...end are completely equivalent 注意{...}和do...end是完全相等的 y = 10 puts "Is y defined inside the block where it is first set?" puts "Yes." if defined? y end puts "Is y defined in the outer context after being set in the block?" puts "No!" unless defined? y # OK, so blocks seem to be like closures: they are closed with respect to variables defined in the context # where they were created, regardless of the context in which they're called. # # But they're not quite closures as we've been using them, because we have no way to pass them around: # "yield" can *only* refer to the block passed to the method it's in. # # We can pass a block on down the chain, however, using &: # 所以块看起来像闭包:他们封闭了定义它们的上下文中的变量,不管他们在哪里调用。 # 但是在我们使用它们时,它们不完全是闭包,因为我们没有办法传递他们。 # “yield”仅仅可以将块传给它所在的方法。 example 4 def six_times(&block) thrice(&block) thrice(&block) end x = 4 six_times { x += 10 } puts "value of x after: #{x}" # So do we have closures? Not quite! We can't hold on to a &block and call it later at an arbitrary # time; it doesn't work. This, for example, will not compile: # # def save_block_for_later(&block) # saved = █ # end # # But we *can* pass it around if we use drop the &, and use block.call(...) instead of yield: # 所以我们是否有闭包?不完全!我们不能保存一个&block,然后稍后在任意时间来调用它。 # 但是如果我们丢掉&,我们就有办法传递它了,使用block,call(...)代替yield。 example 5 def save_for_later(&b) @saved = b # Note: no ampersand! This turns a block into a closure of sorts. 注意:没有&符号!这个做法将一个块变成了一个闭包。 end save_for_later { puts "Hello!" } puts "Deferred execution of a block:" @saved.call @saved.call # But wait! We can't pass multiple blocks to a function! As it turns out, there can be only zero # or one &block_params to a function, and the ¶m *must* be the last in the list. # # None of these will compile: # # def f(&block1, &block2) ... # def f(&block1, arg_after_block) ... # f { puts "block1" } { puts "block2" } # # What the heck? # # I claim this single-block limitation violates the "principle of least surprise." The reasons for # it have to do with ease of C implementation, not semantics. # # So: are we screwed for ever doing anything robust and interesting with closures? # 等等!我们不能传递多个块给一个函数!一个函数只能接收0或1个&block_params参数,并且&block_params必须是最后一个参数。 # 我觉得这个单block违反了最小惊讶原则,因为这很容用C来实现,不是语义上的。 # ---------------------------- Section 2: Closure-Like Ruby Constructs ---------------------------- # --------------------------------- 章节二:闭包风格的Ruby构造器 ---------------------------------- # Actually, no. When we pass a block ¶m, then refer to that param without the ampersand, that # is secretly a synonym for Proc.new(¶m): # 实际上,没有。当我们传递一个块¶m,然后不带&来使用param,这是Proc.new(¶m)的隐式同义词。 example 6 def save_for_later(&b) @saved = Proc.new(&b) # same as: @saved = b end save_for_later { puts "Hello again!" } puts "Deferred execution of a Proc works just the same with Proc.new:" @saved.call # We can define a Proc on the spot, no need for the ¶m: # 我们可以随意定义一个Proc,不需要¶m。 example 7 @saved_proc_new = Proc.new { puts "I'm declared on the spot with Proc.new." } puts "Deferred execution of a Proc works just the same with ad-hoc Proc.new:" @saved_proc_new.call # Behold! A true closure! # # But wait, there's more.... Ruby has a whole bunch of things that seem to behave like closures, # and can be called with .call: # 看!一个真的闭包! # 等等,还有跟精彩的。。。Ruby还有一堆东西的行为看起来像闭包,并且能用.call来调用。 example 8 @saved_proc_new = Proc.new { puts "I'm declared with Proc.new." } @saved_proc = proc { puts "I'm declared with proc." } @saved_lambda = lambda { puts "I'm declared with lambda." } def some_method puts "I'm declared as a method." end @method_as_closure = method(:some_method) puts "Here are four superficially identical forms of deferred execution:" @saved_proc_new.call @saved_proc.call @saved_lambda.call @method_as_closure.call # So in fact, there are no less than seven -- count 'em, SEVEN -- different closure-like constructs in Ruby: # # 1. block (implicitly passed, called with yield) # 2. block (&b => f(&b) => yield) # 3. block (&b => b.call) # 4. Proc.new # 5. proc # 6. lambda # 7. method # # Though they all look different, some of these are secretly identical, as we'll see shortly. # # We already know that (1) and (2) are not really closures -- and they are, in fact, exactly the same thing. # Numbers 3-7 all seem to be identical. Are they just different syntaxes for identical semantics? # ---------------------------- Section 3: Closures and Control Flow ---------------------------- # ---------------------------------- 章节三:闭包后控制流 -------------------------------------- # No, they aren't! One of the distinguishing features has to do with what "return" does. # # Consider first this example of several different closure-like things *without* a return statement. # They all behave identically: # 实际上Ruby有不少于7中不同的闭包风格的构造器 # ... # 尽管他们看起来不一样,有些其实是完全相同的,我们后面马上会看到。 # 我们已经知道(1)和(2)不是真正的闭包,其实(1)和(2)是同样的东西。 # 3-7看上去是完全一样的,他们是不是语义相同的不用语法? example 9 def f(closure) puts puts "About to call closure" result = closure.call puts "Closure returned: #{result}" "Value from f" end puts "f returned: " + f(Proc.new { "Value from Proc.new" }) puts "f returned: " + f(proc { "Value from proc" }) puts "f returned: " + f(lambda { "Value from lambda" }) def another_method "Value from method" end puts "f returned: " + f(method(:another_method)) # But put in a "return," and all hell breaks loose! # 但是加上一个“return”语句,一切都变的不可收拾。 example 10 begin f(Proc.new { return "Value from Proc.new" }) rescue Exception => e puts "Failed with #{e.class}: #{e}" end # The call fails because that "return" needs to be inside a function, and a Proc isn't really # quite a full-fledged function: # 这样的调用会抛出异常因为“return”必须在一个函数内,而一个Proc不是一个真正的完全独立的函数。 example 11 def g result = f(Proc.new { return "Value from Proc.new" }) puts "f returned: " + result #never executed "Value from g" #never executed end puts "g returned: #{g}" # Note that the return inside the "Proc.new" didn't just return from the Proc -- it returned # all the way out of g, bypassing not only the rest of g but the rest of f as well! It worked # almost like an exception. # # This means that it's not possible to call a Proc containing a "return" when the creating # context no longer exists: # 注意在“Proc.new”中的return语句不仅仅从Proc中返回,它将从g方法中返回,不仅绕过g方法中剩余的代码 # 而且f方法中也是一样的!它就像一个异常一样。 # # 这意味着当创建Proc的上下文不存在时,无法调用一个包含“return”的Proc # # 本人的理解: # 在Proc.new中的return语句,会尝试返回到创建它的上下文之后,继续执行,如果创建它的上下文已经“消失”或者返回到了全局上下文,就会出现LocalJumpError。 # # 在#example10中,在全局上下文中用Proc.new创建了一个proc,在f方法中使用call,然后proc会返回到创建它的上下文,即全局上下文,于是出了LocalJumpError。 # 在#example11中,在g方法的上下文中用Proc.new创建了一个proc,在f方法中使用call,然后proc会返回到创建它的上下文,即g方法之后,这种情况不会出异常。 example 12 def make_proc_new begin Proc.new { return "Value from Proc.new" } # this "return" will return from make_proc_new 这个“return”会返回到make_proc_new方法之外 ensure puts "make_proc_new exited" end end begin puts make_proc_new.call rescue Exception => e puts "Failed with #{e.class}: #{e}" end # (Note that this makes it unsafe to pass Procs across threads.) # (注意跨先辰的传递Proc会导致它不安全。) # A Proc.new, then, is not quite truly closed: it depends in the creating context still existing, # because the "return" is tied to that context. # Proc.new,不是一个真正完全的闭包。它依赖于创建它的上下文要一直存在,因为“return”和那个上下文联席在了一起。 # # 本人的理解: # 就和刚刚讲的一样,如果创建它的上下文已经“消失”,在这里make_proc_new方法把创建的proc返回了出去,所以make_proc_new的上下文就不存在了, # 这时候再使用call,也会发生LocalJumpError。 # # Not so for lambda: # lambda就不是这个样子: example 13 def g result = f(lambda { return "Value from lambda" }) puts "f returned: " + result "Value from g" end puts "g returned: #{g}" # And yes, you can call a lambda even when the creating context is gone: # 你可以调用lambda,即使创建它的上下文已经不在。 example 14 def make_lambda begin lambda { return "Value from lambda" } ensure puts "make_lambda exited" end end puts make_lambda.call # Inside a lambda, a return statement only returns from the lambda, and flow continues normally. # So a lambda is like a function unto itself, whereas a Proc remains dependent on the control # flow of its caller. # 在lambda内部,return语句仅仅从lambda中返回,代码流程不会改变。 # 所以lambda就像一个方法一样,而Proc需要依赖于它调用者的控制流。(擦 什么烂翻译 # # A lambda, therefore, is Ruby's true closure. # 所以lambda才是ruby真正的闭包。 # # As it turns out, "proc" is a synonym for either "Proc.new" or "lambda." # Anybody want to guess which one? (Hint: "Proc" in lowercase is "proc.") # 事实证明,"proc"是“Proc.new”或者“lambda”的同义词。 # 有人想猜一猜到底是哪个吗?(提示:“Proc”的小写是“proc”。) example 15 def g result = f(proc { return "Value from proc" }) puts "f returned: " + result "Value from g" end puts "g returned: #{g}" # Hah. Fooled you. # 哈哈,你被耍了。 # # The answer: Ruby changed its mind. If you're using Ruby 1.8, it's a synonym for "lambda." # That's surprising (and also ridiculous); somebody figured this out, so in 1.9, it's a synonym for # Proc.new. Go figure. # 答案是如果你使用ruby1.8,那么它是“lambda”的同义词。 # 这真得让人惊讶(而且荒谬),有些人指出了这一点,所以在ruby1.9,它是“Proc.new”的同义词了。 # I'll spare you the rest of the experiments, and give you the behavior of all 7 cases: # 我就不进行剩下的实验了,但是会给出所有的7种示例。 # # # "return" returns from caller: # 1. block (called with yield) # 2. block (&b => f(&b) => yield) # 3. block (&b => b.call) # 4. Proc.new # 5. proc in 1.9 # # "return" only returns from closure: # 5. proc in 1.8 # 6. lambda # 7. method # ---------------------------- Section 4: Closures and Arity ---------------------------- # --------------------------------- 章节四:闭包和元数 ---------------------------------- # # 注:arity => 一个方法或者函数可以接受的参数个数 # The other major distinguishing of different kinds of Ruby closures is how they handle mismatched # arity -- in other words, the wrong number of arguments. # 另外一个主要辨别不同种类的ruby闭包是他们如何处理不匹配的元数,换句话说,就是不正确的参数个数。 # # In addition to "call," every closure has an "arity" method which returns the number of expected # arguments: # 除了“call”以外,每个闭包还有一个“arity”方法可以返回期望的参数个数。 example 16 puts "One-arg lambda:" puts (lambda {|x|}.arity) puts "Three-arg lambda:" puts (lambda {|x,y,z|}.arity) # ...well, sort of: puts "No-args lambda: " puts (lambda {}.arity) # This behavior is also subject to change in 1.9. #这个行为在1.9中也会有变化。 1.8中是-1,1.9中是0 puts "Varargs lambda: " puts (lambda {|*args|}.arity) # Watch what happens when we call these with the wrong number of arguments: # 看看当我们使用不真确的参数个数来调用他们时,会发生什么。 example 17 def call_with_too_many_args(closure) begin puts "closure arity: #{closure.arity}" closure.call(1,2,3,4,5,6) puts "Too many args worked" rescue Exception => e puts "Too many args threw exception #{e.class}: #{e}" end end def two_arg_method(x,y) end puts; puts "Proc.new:"; call_with_too_many_args(Proc.new {|x,y|}) puts; puts "proc:" ; call_with_too_many_args(proc {|x,y|}) puts; puts "lambda:" ; call_with_too_many_args(lambda {|x,y|}) puts; puts "Method:" ; call_with_too_many_args(method(:two_arg_method)) def call_with_too_few_args(closure) begin puts "closure arity: #{closure.arity}" closure.call() puts "Too few args worked" rescue Exception => e puts "Too few args threw exception #{e.class}: #{e}" end end puts; puts "Proc.new:"; call_with_too_few_args(Proc.new {|x,y|}) puts; puts "proc:" ; call_with_too_few_args(proc {|x,y|}) puts; puts "lambda:" ; call_with_too_few_args(lambda {|x,y|}) puts; puts "Method:" ; call_with_too_few_args(method(:two_arg_method)) # Yet oddly, the behavior for one-argument closures is different.... # 奇怪的是,当闭包只有一个参数时,它们的行为又不一样了。。。 example 18 def one_arg_method(x) end puts; puts "Proc.new:"; call_with_too_many_args(Proc.new {|x|}) puts; puts "proc:" ; call_with_too_many_args(proc {|x|}) puts; puts "lambda:" ; call_with_too_many_args(lambda {|x|}) puts; puts "Method:" ; call_with_too_many_args(method(:one_arg_method)) puts; puts "Proc.new:"; call_with_too_few_args(Proc.new {|x|}) puts; puts "proc:" ; call_with_too_few_args(proc {|x|}) puts; puts "lambda:" ; call_with_too_few_args(lambda {|x|}) puts; puts "Method:" ; call_with_too_few_args(method(:one_arg_method)) # Yet when there are no args... # 当他们没有参数时。。。 example 19 def no_arg_method end puts; puts "Proc.new:"; call_with_too_many_args(Proc.new {||}) puts; puts "proc:" ; call_with_too_many_args(proc {||}) puts; puts "lambda:" ; call_with_too_many_args(lambda {||}) puts; puts "Method:" ; call_with_too_many_args(method(:no_arg_method)) # For no good reason that I can see, Proc.new, proc and lambda treat a single argument as a special # case; only a method enforces arity in all cases. Principle of least surprise my ass. # 没办法解释你和我看到的结果,Proc.new,proc和lambda在面对一个参数时有特殊的处理; # 只有method在任何情况下都强制执行了参数的个数。真是草尼马的最小惊讶原则。 # # 注:#example17-19的结果在ruby1.8和1.9下,1.8下proc和lambda的执行结果完全一样,1.9下proc和Proc.new的结果完全相同。 # ---------------------------- Section 5: Rant ---------------------------- # ------------------------------ 章节五:咆哮 ----------------------------- # # This is quite a dizzing array of syntactic options, with subtle semantics differences that are not # at all obvious, and riddled with minor special cases. It's like a big bear trap from programmers who # expect the language to just work. # 真是让人蛋疼的语法,这些语法上的微妙差异不是那么显而易见的,而且还有不少小的特殊情况。 # # # Why are things this way? Because Ruby is: # 为什么会这样呢?因为ruby是: # # (1) designed by implementation, and # (2) defined by implementation. # 通过实现来设计和定义 (看不明白 # # The language grows because the Ruby team tacks on cool ideas, without maintaining a real spec apart # from CRuby. A spec would make clear the logical structure of the language, and thus help highlight # inconsistencies like the ones we've just seen. Instead, these inconsinstencies creep into the language, # confuse the crap out of poor souls like me who are trying to learn it, and then get submitted as bug # reports. Something as fundamental as the semantics of proc should not get so screwed up that they have # to backtrack between releases, for heaven's sake! Yes, I know, language design is hard -- but something # like this proc/lambda issue or the arity problem wasn't so hard to get right the first time. # Yammer yammer. # (此处省略原作者抱怨的100字。。。) # ---------------------------- Section 6: Summary ---------------------------- # ------------------------------- 章节六:总结 ------------------------------- # # So, what's the final verdict on those 7 closure-like entities? # 所以,那7个闭包风格的实体最终结论如下: # # "return" returns from closure # True closure? or declaring context...? Arity check? # --------------- ----------------------------- ------------------- # 1. block (called with yield) N declaring no # 2. block (&b => f(&b) => yield) N declaring no # 3. block (&b => b.call) Y except return declaring warn on too few # 4. Proc.new Y except return declaring warn on too few # 5. proc <<< alias for lambda in 1.8, Proc.new in 1.9 >>> # 6. lambda Y closure yes, except arity 1 # 7. method Y closure yes # # The things within each of these groups are all semantically identical -- that is, they're different # syntaxes for the same thing: # 下面的分组中的每个在语义上都是相同的,也就是说,只是语法不同的同一个东西。 # # 1. block (called with yield) # 2. block (&b => f(&b) => yield) # ------- # 3. block (&b => b.call) # 4. Proc.new # 5. proc in 1.9 # ------- # 5. proc in 1.8 # 6. lambda # ------- # 7. method (may be identical to lambda with changes to arity checking in 1.9) 在1.9中参数检查和lambda一样 # # Or at least, this is how I *think* it is, based on experiment. There's no authoritative answer other # than testing the CRuby implementation, because there's no real spec -- so there may be other differences # I haven't discovered. # 至少,根据实验,我是这么认为的。除了测试CRuby实现外,也没有其他的官方答案。因为没有真实的规范--所以也许和我发现的有些不同。 # # The final verdict: Ruby has four types of closures and near-closures, expressible in seven syntactic # variants. Not pretty. But you sure sure do cool stuff with them! That's up next.... # 最终结论:Ruby有四个类型的闭包和近似闭包,使用7中语法变形来体现,不是很好,但是你能他们来做一些很cool的东西。 # # This concludes the "Ruby sucks" portion of our broadcast; from here on, it will be the "Ruby is # awesome" portion. # ---------------------------- Section 7: Doing Something Cool with Closures ---------------------------- # ----------------------------------- 章节七:用闭包来做些Cool的东西 ------------------------------------ # Let's make a data structure containing all of the Fibonacci numbers. Yes, I said *all* of them. # How is this possible? We'll use closures to do lazy evaluation, so that the computer only calculates # as much of the list as we ask for. # 我们创建一个数据结构包含所有的菲波那契数。是的,我说“所有的”。 # 这怎么可能?我们将使用闭包来做到延迟求值,所以计算机仅仅会计算我们所要的。 # To make this work, we're going to use Lisp-style lists: a list is a recursive data structure with # two parts: "car," the next element of the list, and "cdr," the remainder of the list. # 要让他工作,我们要使用Lisp风格的lists:一个包含2部分的可递归的数据结构:“car”,list中的下一个元素,“cdr”,list中剩余的元素。 # # For example, the list of the first three positive integers is [1,[2,[3]]]. Why? Because: # # [1,[2,[3]]] <--- car=1, cdr=[2,[3]] # [2,[3]] <--- car=2, cdr=[3] # [3] <--- car=3, cdr=nil # # Here's a class for traversing such lists: # 这有一个类来遍历这样的list example 20 class LispyEnumerable include Enumerable def initialize(tree) @tree = tree end def each while @tree car,cdr = @tree yield car @tree = cdr end end end list = [1,[2,[3]]] LispyEnumerable.new(list).each do |x| puts x end # So how to make an infinite list? Instead of making each node in the list a fully built # data structure, we'll make it a closure -- and then we won't call that closure # until we actually need the value. This applies recursively: the top of the tree is a closure, # and its cdr is a closure, and the cdr's cdr is a closure.... # 所以如和去创建一个无限的list?我们通过创建一个闭包来代替原来在list中内建的基本数据结构。 # 除非我们真得须要数据,否则我们不会调用它。它会是一个递归。。。 example 21 class LazyLispyEnumerable include Enumerable def initialize(tree) @tree = tree end def each while @tree car,cdr = @tree.call # <--- @tree is a closure yield car @tree = cdr end end end list = lambda{[1, lambda {[2, lambda {[3]}]}]} # same as above, except we wrap each level in a lambda 和前面的一样,只是多包了一个lambda LazyLispyEnumerable.new(list).each do |x| puts x end example 22 # Let's see when each of those blocks gets called: # 让我们来看看这些block是什么时候被调用的 list = lambda do puts "first lambda called" [1, lambda do puts "second lambda called" [2, lambda do puts "third lambda called" [3] end] end] end puts "List created; about to iterate:" LazyLispyEnumerable.new(list).each do |x| puts x end # Now, because the lambda defers evaluation, we can make an infinite list: # 现在,因为lamdba的延迟求值,我们可以创建无限list。 example 23 def fibo(a,b) lambda { [a, fibo(b,a+b)] } # <---- this would go into infinite recursion if it weren't in a lambda 这个会进入死循环如果它不在lambda内部 end LazyLispyEnumerable.new(fibo(1,1)).each do |x| puts x break if x > 100 # we don't actually want to print all of the Fibonaccis! 我们实际上不会要打印所有的菲波那契数。 end # This kind of deferred execution is called "lazy evaluation" -- as opposed to the "eager # evaluation" we're used to, where we evaluate an expression before passing its value on. # (Most languages, including Ruby, use eager evaluation, but there are languages (like Haskell) # which use lazy evaluation for everything, by default! Not always performant, but ever so very cool.) # 这就是延迟求值,和我们通常用得立即求值相反,我们在传送值之前就求值了表达式。 # 大部分语言,包括ruby,都是立即求值,但是有些其他语言,比如Haskell,他默认就是对任何都延迟求值。 # 这不是一直是高性能的,但是这非常cool。 # # This way of implementing lazy evaluation is terribly clunky! We had to write a separate # LazyLispyEnumerable that *knows* we're passing it a special lazy data structure. How unsatisfying! # Wouldn't it be nice of the lazy evaluation were invisible to callers of the lazy object? # 这种实现延迟求值的方式是非常笨拙的。我们不得不写一个单独的LazyLispyEnumerable,让他知道我们传了一个 # 特别的延迟数据结构给他。能不能让他更好的处理延迟求值,让他对于调用者隐藏延迟对象? # # As it turns out, we can do this. We'll define a class called "Lazy," which takes a block, turns it # into a closure, and holds onto it without immediately calling it. The first time somebody calls a # method, we evaluate the closure and then forward the method call on to the closure's result. # 事实证明,我们可以这么做。我们定一个叫“Lazy”的类,然后使用block,把它转成闭包,然后保存它。 # 第一次一些人调用了它,我们求取闭包的值,然后跳转到下一个方法调用。 class Lazy def initialize(&generator) @generator = generator end def method_missing(method, *args, &block) evaluate.send(method, *args, &block) end def evaluate @value = @generator.call unless @value @value end end def lazy(&b) Lazy.new &b end # This basically allows us to say: # 你可以这样来使用: # # lazy {value} # # ...and get an object that *looks* exactly like value -- except that value won't be created until the # first method call that touches it. It creates a transparent lazy proxy object. Observe: # 。。。然后就能得到一个看起来像指定的value一样的对象—— 除了value只有在一次调用method的以后才会创建。 # 它创建了一个透明的延迟代理对象。 example 24 x = lazy do puts "<<< Evaluating lazy value >>>" "lazy value" end puts "x has now been assigned" puts "About to call one of x's methods:" puts "x.size: #{x.size}" # <--- .size triggers lazy evaluation .size会触发延迟求值 puts "x.swapcase: #{x.swapcase}" # So now, if we define fibo using lazy instead of lambda, it should magically work with our # original LispyEnumerable -- which has no idea it's dealing with a lazy value! Right? # 现在,我们使用lazy代替lambda来创建菲波那契函数,它应该可以神奇地和我们来原的LispyEnumerable工作。 # LispyEnumerable并不知道如何处理一个lazy value!是不是? example 25 def fibo(a,b) lazy { [a, fibo(b,a+b)] } end LispyEnumerable.new(fibo(1,1)).each do |x| puts x break if x.instance_of?(Lazy) || x > 200 end # 这个方法在ruby 1.8和1.9中不一样 # 1.8中会调用respond_to?(to_a)方法来找to_a,而respond_to又是Object的方法,每个对象都会有,所以不会经过method_missing # 1.9中会直接调用method_missing(to_a)方法来找to_a # 所以1.8只会打印出一个Lazy对象,而1.9中可以顺利执行 # Oops! That didn't work. What went wrong? # 噢,它并没有如我们想的那样工作,哪里出错了? # # The failure started in this line of LispyEnumerable (though Ruby didn't report the error there): # 错误发生在LispyEnumerable的这一行(尽管Ruby没有报错) # # car,cdr = @tree # # Let's zoom in on that result, and see what happened: # 让我们看看到底发生了什么! example 26 car,cdr = fibo(1,1) puts "car=#{car} cdr=#{cdr}" # Here's the problem. When we do this: # 问题就在这里,当我们这样赋值时: # # x,y = z # # ...Ruby calls z.respond_to?(to_a) to see if z is an array. If it is, it will do the multiple # assignment; if not, it will just assign x=z and set y=nil. # Ruby会调用z.respond_to?(to_a)来看看z是否能变成一个数组。如果可以,它会进行多次赋值, # 否则,它只会赋值 x=z 和 y = nil。 # # We want our Lazy to forward the respond_to? call to our fibo list. But it doesn't forward it, # because we used the method_missing to do the proxying -- and every object implements respond_to? # by default, so the method isn't missing! The respond_to? doesn't get forwarded; instead, out Lazy # says "No, I don't respond to to_a; thanks for asking." The immediate solution is to forward # respond_to? manually: # 我们想要的Lazy来转发respond_to?,让它调用到我们的菲波那契。但是它没有做到,因为我们用method_missing # 来进行代理 -- 而每个对象默认都有respond_to?方法,所以无法触发到method_missing!respond_to?方法没有被转发, # 所以Lazy说:“我没有to_a方法,谢谢你的调用。”最快捷的办法是手动转发respond_to?方法。 class Lazy def initialize(&generator) @generator = generator end def method_missing(method, *args, &block) evaluate.send(method, *args, &block) end def respond_to?(method) evaluate.respond_to?(method) end def evaluate @value = @generator.call unless @value @value end end # And *now* our original Lispy enum can work: # 现在我们原来的Lispy enum就能工作了。 example 27 LispyEnumerable.new(fibo(1,1)).each do |x| puts x break if x > 200 end # Of course, this only fixes the problem for respond_to?, and we have the same problem for every other # method of Object. There is a more robust solution -- frightening, but it works -- which is to undefine # all the methods of the Lazy when it's created, so that everything gets forwarded. # 当然,这只是修改了respond_to?的问题,Object的其他方法也有同样的问题。这里有一个更健壮的办法,虽然有点可怕, # 但是它能工作。那就是在Lazy对象创建时取消定义Lazy所有的方法,那样就都能被转发了。 # # And guess what? There's already a slick little gem that will do it: # 其实已经有一个gem做了这样的事: # # http://moonbase.rydia.net/software/lazy.rb/ # # Read the source. It's fascinating. # 看看它的源码把,非常让人着迷。 # ---------------------------- Section 8: Wrap-Up ---------------------------- # ------------------------------- 章节八:总结 ------------------------------- # So sure, this was all entertaining -- but is it good for anything? # 这就是所有了 -- 那么是不是它有利于任何事? # # Well, suppose you have an object which requires a network or database call to be created, or will # use a lot of memory once it exists. And suppose that it may or may not be used, but you don't know # at the time it's created whether it will be. Making it lazy will prevent it from consuming resources # unless it needs to. Hibernate does this to prevent unnecessary DB queries, and it does it with more or # less arbitrary Java objects (i.e. unlike ActiveRecord, it doesn't depend on a base class to do its # lazy loading). Ruby can do the same thing, but with a lot less code! # 假设你有一个对象须要一个网络或者数据库的调用才能被创建,或者会一旦创建会占用大量内存。你也不知道什么 # 时候会用到它。使用lazy将防止它消耗资源,除非它真得需要。Hibernate使用延迟加载来阻止不必要的数据库查询, # 而Hibernate做到它需要或多或少的Java对象。(不像ActiveRecord,它依赖于一个base class来做到延迟加载) # Ruby可以使用更少的代码做到相同的事情。 # # # That's just an example. Use your imagination. # 这只是个示例,发挥你的想象力。 # # If you're a functional langauge geek, and enjoyed seeing Ruby play with these ideas from Lisp and # Haskell, you may enjoy this thread: # 如果你是个函数式语言的极客,并且喜欢使用Ruby的这些来自于Lisp和Haskell的特性,你也许会想加入 # # http://redhanded.hobix.com/inspect/curryingWithArity.html # # OK, I'll stop making your brain hurt now. Hope this has been a bit enlightening! The experience # of working it out certainly was for me. # 好了,我不再伤害你的大脑了。希望这能够给你一点启发!理解这些的经验对我非常有用。 # # Paul
发表评论
-
在Rails中使用Pry
2012-02-07 06:43 2177Pry可看成是IRB的加强版。支持语法高亮等特点。 1、在Ge ... -
camping 一个小巧的ruby web framework
2012-02-04 04:01 1791https://github.com/camping/camp ... -
Markaby (Markup as Ruby)
2012-02-04 03:58 1179http://markaby.rubyforge.org/ ... -
Ruby的一些疑问
2012-01-26 01:01 14351、网点看到一断程序, def m1(a) puts 'i ... -
RSpec测试框架
2012-01-10 12:59 2991#参考http://www.slideshare.net/ih ... -
[转]Ruby - DUP vs CLONE
2012-01-09 12:57 1834http://railsblogger.blogspot.co ... -
[转]eval, class_eval, instance_eval和binding
2012-01-09 12:10 1099http://www.cnblogs.com/rubylouv ... -
[转]Ruby中的binding
2012-01-09 11:50 1599http://kkito.cn/index.php/blog/ ... -
[转]Method visibility in Ruby
2012-01-04 12:37 1168From:http://weblog.jamisbuck.or ... -
[转]浅谈Ruby on Rails中的include和extend
2011-12-30 02:20 1057http://developer.51cto.com/art/ ... -
[转]Ruby中的Rake任务详述
2011-12-29 03:47 1771Trackback: http://tb.blog.csdn. ... -
ruby技巧
2011-12-26 10:40 0ruby小技巧之 http://kenbeit.com/pos ... -
[转]Ruby中的多态
2011-12-20 11:56 0原文http://kkito.cn/index.php ... -
ruby中实现闭包
2011-12-20 09:10 958ruby中实现闭包很简单 如果一个方法中返回一个proced ... -
ruby中星号的使用
2011-12-20 05:54 1928ruby中星号的使用 1、数字乘法 2 * 3 = 6 2 ... -
[转]ruby的include与extend
2011-12-20 05:09 903原文http://www.cnblogs.com/rubylo ... -
[转]Ruby常用的内部变量
2011-12-20 03:59 952Ruby常用的内部变量 原文http://www.cnblog ... -
ruby与javascript面向对象编程的比较
2011-12-06 14:05 3351原文:http://howtonode.org/object- ... -
解决ruby中文乱码
2011-12-06 04:20 8012在文件头加上#encoding=UTF-8,示例: #enco ... -
[转帖]重新认识Ruby的多态和duck type
2011-12-05 11:56 986多态在Java中是如何定义的? 原文:http://devel ...
相关推荐
在规则引擎中,Ruby 的闭包使用特别频繁,而且有 block,Proc和 lambda 等后几种形式的用法,很让人困惑。为了深入理解代码,再次认真学习了一下 Ruby 的闭包,特别是 block,proc 和 lambda 几种用法的异同,这次的...
同时,也可能涵盖了Ruby的高级特性,如元编程、符号、 Proc对象、块和闭包等。 "The Ruby Way.chm"则可能是《The Ruby Way》一书的电子版,这是一本深入介绍Ruby编程实践的书籍,它不仅讲解了Ruby的基本语法,还...
通过这个中文手册,开发者不仅可以学习到Ruby的基本语法,还可以了解到更高级的主题,如元编程、闭包、 Blocks、Proc对象和Symbol,以及如何利用Ruby的灵活性来创建高效、简洁的代码。同时,了解RGSS可以让开发者...
在Ruby中,闭包被广泛使用,并且表现得非常直观。Ruby的闭包(也称为块)是匿名函数,它们可以作为参数传递给其他函数,或者嵌套在其他函数内部。清单1展示了如何使用闭包(块)遍历一个范围并打印语句。清单2显示了...
《精通Ruby闭包》是Benjamin Tan Wei Hao撰写的一本指导书,旨在帮助读者深入了解Ruby编程语言中的闭包概念,包括块(Blocks)、Proc对象以及lambda表达式。本书着重介绍这些Ruby闭包的具体用途和行为,通过示例和...
- **块和闭包**:Ruby中的块和闭包提供了强大的代码组织和复用能力,是实现高阶编程的关键。 - **元编程**:Ruby允许在运行时修改和创建类,这为自定义行为和构建高度定制的框架提供了可能。 Rails框架则进一步提升...
3. **块、 Proc 和 Lambda**:Ruby中的块是一段可以被传递和执行的代码,类似于其他语言中的闭包或匿名函数。Proc和Lambda是两种表示代码块的数据结构,它们可以存储和调用代码块,而且在行为上与函数类似。 4. **...
7. **闭包和元编程**:Ruby的闭包(Proc和Lambda)以及动态特性(如eval、instance_variable_get/set、method_missing等)让元编程变得非常强大,允许在运行时修改和扩展代码。 8. **正则表达式**:Ruby的Regexp类...
Ruby的特色包括块(blocks)、闭包(closures)、元类(metaclasses)以及模块(modules),这些特性使得代码可读性高,同时也提供了高度的灵活性。 2. **SaaS软件工程**:SaaS是一种软件交付模式,用户通过互联网...
Ruby的核心特点包括它的灵活性、块语法、闭包以及面向对象特性。Ruby的元编程能力也相当强大,允许在运行时修改或创建代码,这种能力使得Ruby在许多领域都有广泛的应用。 Ruby的中文文档,如“ruby中文文档.chm”,...
Ruby中的类和模块是构建对象的基础,类用于定义对象的结构和行为,模块则用于组织代码和实现多继承。 3. **方法**:Ruby的方法可以看作是对象的行为,可以定义在类、模块或者对象上。方法调用可以用点操作符(`.`)...
Ruby的块和闭包是其独特之处,它们提供了强大的控制流和数据处理能力,这是Java中匿名内部类和接口不能比拟的。 其次,Ruby的语法简洁明了,例如,方法调用可以省略括号,变量声明不需要特定关键字,这使得代码看...
Ruby中的闭包和`Proc`对象非常适合实现命令模式。 15. **解释器模式**:给定一种语言,定义其文法的一种表示,并实现一个解释器,该解释器使用该表示来解释语言中的句子。Ruby本身就是一种动态语言,其本身就是一个...
闭包(Closure),是指未绑定到任何对象的自由代码,闭包中的代码与任何对象和全局变量无关,只与执行此段代码的上下文相关。 今天我们简要的看一下ruby中的闭包实现。 Ruby中的闭包实现有:Block,Proc,Lambada。 ...
在Ruby中,Functo提供了一种方式来创建“闭包”(Closure)或者称为“lambda”或“Proc”,这些是可以保存其执行环境的代码块。闭包可以捕获其定义时的上下文,使得它们在稍后的时间点依然能够访问到那些变量,即使...
2. 闭包(Closures):在Ruby中,闭包是匿名函数的概念,它能够捕获定义时的上下文环境。闭包是元编程中不可或缺的元素,它允许开发者在不同的上下文之间传递代码块。 3. 领域特定语言(DSLs)的定义:领域特定语言...
4. **闭包和块**:Ruby支持闭包和迭代器块,使得循环和迭代操作更加灵活。 5. **元编程**:Ruby支持元编程技术,可以在运行时修改类和模块的定义。 6. **RubyGems**:Ruby的包管理器,用于安装和管理第三方库。 7. *...
Ruby还支持闭包,这是一种强大的工具,可以捕获和存储当前环境的变量,这在函数式编程中尤其有用。 "不墨迹,大量练习"是该教程的一大特点。学习编程不仅仅是理论知识的积累,更重要的是动手实践。书中的每个小节...
Ruby的闭包特性使得代码更加简洁和高效。 关于源代码部分,读者可以配合教程实践编程,加深理解。这些源代码可能涵盖简单的“Hello, World!”程序,到更复杂的类定义和模块应用,以及文件操作、网络编程、数据库...
5. **闭包**:Ruby中的 Proc 和 lambda 实现了闭包,能够捕获其定义时的上下文环境,这在函数式编程中非常有用。 6. **模块(Module)**:Ruby的模块用于实现命名空间,代码重用和混合(mixin)功能。一个类可以...