Блоки в глупых блоках
В качестве упражнения мне дали следующие тесты:
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
Я смог решить их с помощью следующего кода:
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
Однако я некоторые из этих концепций не очень хорошо понимаю. Например:
- Что именно обозначает символ & в параметре &block?
- Точно так же, что такое block.call и где находится реальный объект блока, который я предполагаю для его вызова?
- Могу ли я теоретически использовать другой метод на блоке, если я хочу достичь чего-то еще?
- Также, где я могу узнать немного больше о блоках
Это упражнение было немного выше моих нынешних знаний.
1 ответ
Это означает "это параметр блока". Вы не обязаны называть это
&block
поэтому должен быть способ отделить его от других аргументов. Та же запись используется для передачи аргументов функции в виде блока, в отличие от обычных аргументов (см. Ниже).block.call
это то же самое, чтоyield
, Разница в том, что вы можете использоватьblock
получить доступ к самому блоку, не вызывая его немедленно. Например, вы можете сохранить блок для последующего выполнения. Это распространенная модель, известная как ленивая оценка.Да, вы также можете передать разные вещи, чем
do
/end
блок как&block
параметр. Ниже приведены некоторые примеры.@UriAgassi дал вам отличную ссылку.
Вот некоторые другие вещи, которые вы можете передать в качестве аргумента блока. Во-первых, просто простой метод, который принимает блок для демонстрации:
def reverser(&block)
block.call.reverse
end
Теперь вы можете передать стандартный блок
reverser do
"hello"
end
#=> "olleh"
Или, в альтернативном синтаксисе блока, используется для встроенного стиля
reverser { "hello" }
#=> olleh
Вы также можете передать лямбда или процесс, который похож на блок. Используя обозначение & block, вы можете передать переменную в качестве аргумента блока:
my_block = lambda { "hello world!" }
reverser(&my_block)
#=> "!dlrow olleh"
Или в альтернативном лямбда-синтаксисе
my_block = -> { "hello world!" }
reverser(&my_block)
#=> "!dlrow olleh"
Вы даже можете взять существующий метод и передать его в качестве аргумента блока, здесь вы можете увидеть большое преимущество блоков: они оцениваются при выполнении 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"
Вы можете делать классные вещи с этим, например:
[1, 2, 3].each(&method(:puts))
1
2
3
#=> [1, 2, 3]
Но помните, чтобы не переусердствовать, 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