愚蠢的块中的块 rspec 测试

use*_*950 2 ruby methods ruby-on-rails block

作为练习,我进行了以下测试:

require "silly_blocks"

describe "some silly block functions" do

  describe "reverser" do
    it "reverses the string returned by the default block" do
      result = reverser do
        "hello"
      end
      result.should == "olleh"
    end

    it "reverses each word in the string returned by the default block" do
      result = reverser do
        "hello dolly"
      end
      result.should == "olleh yllod"
    end
  end

  describe "adder" do
    it "adds one to the value returned by the default block" do
      adder do
        5
      end.should == 6
    end

    it "adds 3 to the value returned by the default block" do
      adder(3) do
        5
      end.should == 8
    end
  end

  describe "repeater" do
    it "executes the default block" do
      block_was_executed = false
      repeater do
        block_was_executed = true
      end
      block_was_executed.should == true
    end

    it "executes the default block 3 times" do
      n = 0
      repeater(3) do
        n += 1
      end
      n.should == 3
    end

    it "executes the default block 10 times" do
      n = 0
      repeater(10) do
        n += 1
      end
      n.should == 10
    end

  end

end
Run Code Online (Sandbox Code Playgroud)

我能够用以下代码解决它们:

def reverser
k = []
  x = yield.split(" ")
  x.each do |y|
    n = y.reverse
  k.push(n)
end

m = k.join(" ")
m
end

def adder(num=1, &block)
  block.call + num
end

def repeater(num=1, &block)
    for i in (1..num) do
        block.call
    end
end
Run Code Online (Sandbox Code Playgroud)

然而,我对其中一些概念不太理解。例如:

  1. &block参数中的&符号到底是什么意思?
  2. 同样,什么是 block.call 以及我假设其调用的实际块对象在哪里?
  3. 如果我想实现其他目标,理论上我可以使用另一种方法吗?
  4. 另外我在哪里可以了解更多有关块的信息

这个练习有点超出我目前的知识。

Pat*_*ity 5

  1. 它的意思是“这是块参数”。您不必一定要调用它&block,因此需要有一种方法将其与其他参数分开。使用相同的符号将参数作为块传递给函数,而不是普通参数(见下文)

  2. block.call与 完全相同yield。不同之处在于,您可以使用它block来访问块本身,而无需立即调用它。例如,您可以存储该块以供以后执行。这是一种称为惰性求值的常见模式。

  3. 是的,您还可以传递与do/end块不同的东西作为&block参数。请参阅下面的一些示例。

  4. @UriAgassi 给了你一个很好的链接。

以下是您可以作为块参数传递的其他一些内容。首先,只是一个简单的方法,拿一个块来进行演示:

def reverser(&block)
  block.call.reverse
end
Run Code Online (Sandbox Code Playgroud)

您现在可以传递一个标准块

reverser do
  "hello"
end
#=> "olleh"
Run Code Online (Sandbox Code Playgroud)

或者,在替代块语法中,用于内联样式

reverser { "hello" }
#=> olleh
Run Code Online (Sandbox Code Playgroud)

您还可以传递 lambda 或 proc,这与块类似。通过使用 &block 表示法,您可以将变量作为块参数传递:

my_block = lambda { "hello world!" }
reverser(&my_block)
#=> "!dlrow olleh"
Run Code Online (Sandbox Code Playgroud)

或者,用替代的 lambda 语法

my_block = -> { "hello world!" }
reverser(&my_block)
#=> "!dlrow olleh"
Run Code Online (Sandbox Code Playgroud)

您甚至可以采用现有方法并将其作为块参数传递,在这里您可以看到块的巨大优势:它们在执行 block.call 时进行评估,而不是在加载代码时进行评估。这里这意味着字符串每次都会相应地改变。

def foobar
  "foobar at #{Time.now}"
end
reverser(&method(:foobar))
#=> "0020+ 15:42:90 02-50-4102 ta raboof"
#=> "0020+ 31:52:90 02-50-4102 ta raboof"
Run Code Online (Sandbox Code Playgroud)

你可以用它做一些很酷的事情,例如:

[1, 2, 3].each(&method(:puts))
1
2
3
#=> [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

但请记住不要做得太过分,Ruby 注重的是富有表现力和可读性的代码。在增强代码时使用这些技术,但如果可能,请使用更简单的方法。

最后,这也是一个惰性求值的例子:

class LazyReverser
  def initialize(&block)
    @block = block
  end

  def reverse
    @block.call.reverse
  end
end

reverser = LazyReverser.new do
  # some very expensive computation going on here,
  # maybe we do not even need it, so lets use the
  # lazy reverser!

  "hello dolly"
end

# now go and do some other stuff

# it is not until later in the program, that we can decide
# whether or not we even need to call the block at all
if some_condition
  reverser.reverse
  #=> "yllod olleh"
else
  # we did not need the result, so we saved ourselves
  # the expensive computation in the block altogether!
end
Run Code Online (Sandbox Code Playgroud)