简体   繁体   中英

Ruby default block and yield

I am working on the following problem:

describe "some silly block functions" do
  describe "reverser" do
    it "reverses the string returned by the default block" do
      result = reverser do
        "hello"
      end

      expect(result).to eq("olleh")
    end

From my understanding this should reverse a string. My code is as follows:

def reverser
    yield "hello"
end

reverser do |i|
    puts i.reverse
end

This simply returns "hello". I may be missing some fundamental concepts here about how yield, blocks, and functions all interact. How do I going about doing what I am trying to accomplish?

The answers are good and correct but perhaps it still do not help.

You should start with your spec:

it "reverses the string returned by the default block"

So, it's very clear what your method should do:

def reverser
  # should reverse the string returned by the default block
end

Let's now see how to achieve it. Ok, it should reverse something. But what? Let's see:

string returned by the default block

This suggests that we need to execute the default block and get its returned value. Let's see what the docs say:

yield - Called from inside a method body, yields control to the code block (if any) supplied as part of the method call. ... The value of a call to yield is the value of the executed code block.

So, it seems that your method needs to perform a yield . It will execute a block and return the value the block returns. So, just put a yield there.

def reverser
  yield
end

If you run your spec, it will complain - you will see that the string is still not reversed. So, that's whats left for your method to do:

def reverser
  yield.reverse
end

and that's it.

You need to include the logic of reversing the string in reverser .

def reverser
  yield.reverse
end

But why bothering using block anyway? It's much clearer to use a normal parameter.

def reverser(str)
  str.reverse
end

reverser('hello')  #=> olleh

If you want to put the string to reverse in the block, then you need to get the result of calling the block and reverse it.

def reverser(&block)
  block.call.reverse
end

irb(main):009:0> result = reverser do
irb(main):010:1*   "hello"
irb(main):011:1> end
=> "olleh"

I know it's been a year but this hasn't been answered right.

def reverser
  out = []
  yield.split.each{|word| out << word.reverse}
  out.join(" ")
end

I'm pretty sure it has to do with scope

I agree with the above responses - they make the most sense. but want to add why your code isn't working and how to fix it:

expect(result).to eq("olleh")

So according to that you want result to return a string. Is it doing that?

  1. puts returns nil. when you have puts at the end of a method - be aware that the method will return nil. It's insidious because sometimes the results are not what is expected.
  2. but you are expecting it to return 'olleh'
  3. get rid of the puts and it should work like you expect (untested)
 def reverser yield "hello" end reverser do |i| i.reverse # NOTE THAT THE PUTS is missing here end 

I think that's what you are looking for.

edit: Please test and let me know because some folks think I have the answer completely wrong! of course you'd not want to rely on the particular block that you are using as a design point, but this should give you an idea of why it wasn't working

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM