Нужна помощь в понимании конкретного блока Ruby

#ruby

#ruby

Вопрос:

На днях я ввел в свой код ошибку, показанную ниже, и хотел лучше понять последствия.

 File.open('test.txt').readlines do |line|
  puts "#{line} test"
end
 

File.open.readlines работает точно так, как ожидалось, и возвращает массив, содержащий все строки в файле. Но сразу после этого массива находится блок. Например, массив, возвращаемый readlines, не вызывает методов-членов each . Я предполагаю, что блок ничего не вызывает, поэтому он ничего не делает. На других языках это могло вызвать предупреждение компилятора или в C блок будет считаться вложенной областью и будет выполняться. Но, как показано, Ruby (1.9.2) успешно работает без ошибок и не генерирует никаких выходных puts данных.

Для полноты картины приведем исправленную версию.

 File.open('test.txt').each_line do |line|
  puts "#{line} test"
end 
 

Я хотел бы понять поведение первого примера. Верно ли мое предположение о том, что блок по существу анонимный и не выполнялся, потому что его ничто не вызывало?

Комментарии:

1. Что мне кажется странным, так это то, что если я попытаюсь ['a','b','c'] {|s| puts "#{s} test"} , то получу сообщение об ошибке. readlines также возвращает массив, но он выполняется без ошибок.

2. Да! Я обнаружил то же самое. Это одна из причин, по которой я опубликовал этот вопрос.

Ответ №1:

Вот что происходит в вашем конкретном примере. Когда вы вызываете

 File.open('test.txt').readlines do |line|
  puts "#{line} test"
end
 

Блок передается методу readlines. Блок не передается в массив, который возвращается методом readlines . Это допустимый случай, и, следовательно, интерпретатор ruby не жалуется.

Вот более простой сценарий, иллюстрирующий это

Допустим, у меня был класс, определенный следующим образом

  class Foo
     def bar
     end 
 end
 

Теперь, если я позвоню

  Foo.new.bar {puts "hello"}
 

Интерпретатор не выдаст никакой ошибки, и сообщение «привет» не будет напечатано.

Если я уступлю блоку в bar следующим образом

  class Foo
     def bar
         yield
     end 
 end
 

Затем будут выполнены puts, и вы должны увидеть напечатанный hello

  Foo.new.bar {puts "hello"} # prints hello
 

В итоге блоки передаются методам, а не объектам, и вы можете передать блок любому методу в ruby, даже тому, который этого не ожидает.

Комментарии:

1. В этом есть смысл. Очень хороший ответ. Спасибо, потому что мне тоже было интересно.

2. Ах, это тонкость, которую я не учел. Спасибо.