답변:
예, 처음에는 약간 수수께끼입니다.
Ruby에서 메소드는 임의의 코드 세그먼트를 수행하기 위해 코드 블록을 수신 할 수 있습니다.
메소드가 블록을 예상하면 yield
함수 를 호출하여 블록을 호출합니다 .
예를 들어 목록을 반복하거나 사용자 정의 알고리즘을 제공하는 데 매우 편리합니다.
다음 예제를 보자.
Person
이름으로 초기화 된 클래스 를 정의하고 do_with_name
호출 할 때 name
수신 된 블록에 속성을 전달 하는 메소드를 제공합니다 .
class Person
def initialize( name )
@name = name
end
def do_with_name
yield( @name )
end
end
이를 통해 해당 메소드를 호출하고 임의 코드 블록을 전달할 수 있습니다.
예를 들어, 이름을 인쇄하려면 다음을 수행하십시오.
person = Person.new("Oscar")
#invoking the method passing a block
person.do_with_name do |name|
puts "Hey, his name is #{name}"
end
인쇄 할 것 :
Hey, his name is Oscar
블록은 매개 변수로라는 변수를받습니다 name
(NB는이 변수를 원하는대로 호출 할 수 있지만 호출하는 것이 좋습니다 name
). 코드가 호출되면 yield
이 매개 변수를 값으로 채 웁니다 @name
.
yield( @name )
다른 작업을 수행하기 위해 다른 블록을 제공 할 수 있습니다. 예를 들어, 이름을 바꾸십시오.
#variable to hold the name reversed
reversed_name = ""
#invoke the method passing a different block
person.do_with_name do |name|
reversed_name = name.reverse
end
puts reversed_name
=> "racsO"
우리는 정확히 같은 방법 ( do_with_name
)을 사용했습니다-그것은 다른 블록 일뿐입니다.
이 예는 사소한 것입니다. 더 흥미로운 사용법은 배열의 모든 요소를 필터링하는 것입니다.
days = ["monday", "tuesday", "wednesday", "thursday", "friday"]
# select those which start with 't'
days.select do | item |
item.match /^t/
end
=> ["tuesday", "thursday"]
또는 문자열 크기를 기준으로 사용자 정의 정렬 알고리즘을 제공 할 수도 있습니다.
days.sort do |x,y|
x.size <=> y.size
end
=> ["monday", "friday", "tuesday", "thursday", "wednesday"]
이것이 당신이 그것을 더 잘 이해하는 데 도움이되기를 바랍니다.
BTW, 블록이 옵션 인 경우 다음과 같이 호출해야합니다.
yield(value) if block_given?
선택 사항이 아닌 경우 호출하십시오.
편집하다
@hmak는 다음 예제에 대해 repl.it을 작성했습니다. https://repl.it/@makstaks/blocksandyieldsrubyexample
racsO
되는지 the_name = ""
"Oscar"
(안 대답 매우 분명하다)
person.do_with_name {|string| yield string, something_else }
Ruby에서 메소드는 일반 인수 외에 블록이 제공되는 방식으로 호출되었는지 확인할 수 있습니다. 일반적으로이 block_given?
방법을 사용하여 수행 하지만 &
최종 인수 이름 앞에 앰퍼샌드 ( )를 접두어로 추가하여 블록을 명시 적 Proc로 나타낼 수도 있습니다 .
블록으로 메소드를 호출 yield
하면 필요한 경우 일부 인수를 사용하여 블록을 제어 (블록 호출) 할 수 있습니다. 다음을 보여주는이 예제 방법을 고려하십시오.
def foo(x)
puts "OK: called as foo(#{x.inspect})"
yield("A gift from foo!") if block_given?
end
foo(10)
# OK: called as foo(10)
foo(123) {|y| puts "BLOCK: #{y} How nice =)"}
# OK: called as foo(123)
# BLOCK: A gift from foo! How nice =)
또는 특수 블록 인수 구문을 사용하십시오.
def bar(x, &block)
puts "OK: called as bar(#{x.inspect})"
block.call("A gift from bar!") if block
end
bar(10)
# OK: called as bar(10)
bar(123) {|y| puts "BLOCK: #{y} How nice =)"}
# OK: called as bar(123)
# BLOCK: A gift from bar! How nice =)
누군가가 여기에 진정으로 자세한 답변을 제공 할 가능성은 있지만, Robert Sosinski 의이 게시물 은 항상 블록, 프로세스 및 람다 사이의 미묘함에 대한 훌륭한 설명이라는 것을 알았습니다.
연결하려는 게시물이 루비 1.8과 관련이 있다고 생각합니다. 루비 1.9에서는 블록 변수가 블록에 국한된 것과 같은 일부 사항이 변경되었습니다. 1.8에서는 다음과 같은 것을 얻을 수 있습니다.
>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Goodbye"
1.9가 당신에게 줄 것입니다 :
>> a = "Hello"
=> "Hello"
>> 1.times { |a| a = "Goodbye" }
=> 1
>> a
=> "Hello"
이 컴퓨터에는 1.9가 없으므로 위의 오류가있을 수 있습니다.
나는 당신이 이미 위대한 대답에 그런 식으로 일을하는 이유를 추가하고 싶었습니다.
어떤 언어에서 왔는지 모르지만 정적 언어라고 가정하면 이런 종류의 것이 익숙해 보일 것입니다. 이것은 자바에서 파일을 읽는 방법입니다
public class FileInput {
public static void main(String[] args) {
File file = new File("C:\\MyFile.txt");
FileInputStream fis = null;
BufferedInputStream bis = null;
DataInputStream dis = null;
try {
fis = new FileInputStream(file);
// Here BufferedInputStream is added for fast reading.
bis = new BufferedInputStream(fis);
dis = new DataInputStream(bis);
// dis.available() returns 0 if the file does not have more lines.
while (dis.available() != 0) {
// this statement reads the line from the file and print it to
// the console.
System.out.println(dis.readLine());
}
// dispose all the resources after using them.
fis.close();
bis.close();
dis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
전체 스트림 체인을 무시하고 아이디어는 이쪽
이것이 루비에서하는 방법입니다.
File.open("readfile.rb", "r") do |infile|
while (line = infile.gets)
puts "#{counter}: #{line}"
counter = counter + 1
end
end
전혀 다릅니다. 이걸 분해
여기서 1 단계와 2 단계를 처리하는 대신 기본적으로 다른 클래스에 위임합니다. 보시다시피, 작성해야하는 코드의 양이 급격히 줄어들어 읽기가 쉬워지고 메모리 누수 나 파일 잠금이 지워지지 않을 가능성이 줄어 듭니다.
이제는 자바에서 비슷한 것을 할 수없는 것과는 달리 실제로 사람들은 수십 년 동안 그것을 해왔습니다. 이것을 전략 패턴 이라고합니다 . 차이점은 블록이 없으면 파일 예제와 같은 간단한 방법으로 작성 해야하는 클래스 및 메소드의 양으로 인해 전략이 과도하게 사용된다는 것입니다. 블록을 사용하면 그렇게 간단하고 우아한 방식으로 코드를 구성하지 않는 것이 합리적입니다.
이것은 블록이 사용되는 유일한 방법은 아니지만 다른 것 (예 : form_for api에서 레일에서 볼 수있는 빌더 패턴)은 머리를 감싸면 무슨 일이 일어나고 있는지 분명해야합니다. 블록을 볼 때 일반적으로 메소드 호출이 원하는 것으로 가정하고 블록이 원하는 방식을 설명하는 것이 안전합니다.
File.readlines("readfile.rb").each_with_index do |line, index| puts "#{index + 1}: #{line}" end
자바 사용자들 에게는 더욱 웃어 보자 .
IO.foreach('readfile.rb').each_with_index { |line, index| puts "#{index}: #{line}" }
(메모리 문제가 없음)
이 기사 가 매우 유용하다는 것을 알았습니다 . 특히 다음 예제는
#!/usr/bin/ruby
def test
yield 5
puts "You are in the method test"
yield 100
end
test {|i| puts "You are in the block #{i}"}
test do |i|
puts "You are in the block #{i}"
end
다음 출력을 제공해야합니다.
You are in the block 5
You are in the method test
You are in the block 100
You are in the block 5
You are in the method test
You are in the block 100
따라서 기본적으로 yield
루비를 호출 할 때마다 do
블록 또는 내부 에서 코드를 실행합니다 {}
. 매개 변수가 제공되면 매개 변수로 제공 yield
됩니다.do
블록에 .
저에게는 이것이 do
블록이 무엇을하고 있는지 실제로 이해 한 것은 이번이 처음이었습니다 . 기본적으로 함수가 반복 또는 함수 구성을 위해 내부 데이터 구조에 액세스 할 수있는 방법입니다.
따라서 레일에있을 때 다음을 작성하십시오.
respond_to do |format|
format.html { render template: "my/view", layout: 'my_layout' }
end
(내부) 파라미터로 블록 respond_to
을 생성 하는 기능을 실행합니다 . 그런 다음 이 내부 변수 에서 함수 를 호출 하면 명령 을 실행하기위한 코드 블록이 생성 됩니다. 참고 가 요청한 파일 형식 인 경우에만 얻을 것입니다. (기술 :이 함수 는 소스 에서 볼 수있는 것처럼 실제로 사용 하지 않지만 기능은 본질적으로 동일합니다. 이 질문 은 토론을 참조하십시오 .) 이것은 함수가 초기화를 수행 한 다음 호출 코드에서 입력을 가져 오는 방법을 제공합니다. 그런 다음 필요한 경우 처리를 계속하십시오.do
format
.html
render
.html
block.call
yield
또는 다르게 말하면 익명 함수를 인수로 사용하여 자바 스크립트에서 호출하는 함수와 비슷합니다.
루비에서 블록은 기본적으로 어떤 방법 으로든 전달되고 실행될 수있는 코드입니다. 블록은 항상 메소드와 함께 사용되며 일반적으로 인수로 데이터를 공급합니다.
블록은 Ruby gem (Rail 포함)과 잘 작성된 Ruby 코드에서 널리 사용됩니다. 그것들은 객체가 아니므로 변수에 할당 할 수 없습니다.
블록은 {} 또는 do..end로 묶인 코드입니다. 일반적으로 중괄호 구문은 한 줄 블록에 사용해야하고 do..end 구문은 여러 줄 블록에 사용해야합니다.
{ # This is a single line block }
do
# This is a multi-line block
end
모든 메소드는 암시 적 인수로 블록을 수신 할 수 있습니다. 메소드 내의 yield 문에 의해 블록이 실행됩니다. 기본 구문은 다음과 같습니다.
def meditate
print "Today we will practice zazen"
yield # This indicates the method is expecting a block
end
# We are passing a block as an argument to the meditate method
meditate { print " for 40 minutes." }
Output:
Today we will practice zazen for 40 minutes.
yield 문에 도달하면 meditate 메서드가 블록에 대한 제어를 생성하고 블록 내의 코드가 실행되고 제어가 메서드로 반환되어 yield 문 바로 다음에 실행이 다시 시작됩니다.
메소드에 yield 문이 포함 된 경우 호출시 블록을 수신 할 것으로 예상됩니다. 블록이 제공되지 않으면 yield 문에 도달하면 예외가 발생합니다. 블록을 선택적으로 만들고 예외가 발생하지 않도록 할 수 있습니다.
def meditate
puts "Today we will practice zazen."
yield if block_given?
end meditate
Output:
Today we will practice zazen.
메소드에 여러 블록을 전달할 수 없습니다. 각 방법은 하나의 블록 만 수신 할 수 있습니다.
http://www.zenruby.info/2016/04/introduction-to-blocks-in-ruby.html 에서 더 많은 것을 보십시오
수확량에 대해 두 가지 점이 있습니다. 먼저, 여기에 많은 답변이 수율을 사용하는 방법으로 블록을 전달하는 다른 방법에 대해 이야기하지만 제어 흐름에 대해서도 이야기합시다. 블록에 여러 번 생성 할 수 있기 때문에 특히 관련이 있습니다. 예를 살펴 보겠습니다.
class Fruit
attr_accessor :kinds
def initialize
@kinds = %w(orange apple pear banana)
end
def each
puts 'inside each'
3.times { yield (@kinds.tap {|kinds| puts "selecting from #{kinds}"} ).sample }
end
end
f = Fruit.new
f.each do |kind|
puts 'inside block'
end
=> inside each
=> selecting from ["orange", "apple", "pear", "banana"]
=> inside block
=> selecting from ["orange", "apple", "pear", "banana"]
=> inside block
=> selecting from ["orange", "apple", "pear", "banana"]
=> inside block
각 메소드가 호출 될 때 한 행씩 실행됩니다. 이제 3.times 블록에 도달하면이 블록이 3 번 호출됩니다. 수율을 호출 할 때마다 이 수율은 각 방법을 호출 한 방법과 관련된 블록에 연결됩니다. yield가 호출 될 때마다 클라이언트 코드에서 각 메소드의 블록으로 제어권을 리턴합니다. 블록 실행이 완료되면 3.times 블록으로 돌아갑니다. 그리고 이것은 3 번 발생합니다. 따라서 클라이언트 코드의 블록은 수율이 3 개의 개별 시간으로 명시 적으로 호출되므로 3 개의 개별 경우에 대해 호출됩니다.
두 번째 요점은 enum_for와 yield입니다. enum_for는 Enumerator 클래스를 인스턴스화하고이 Enumerator 객체는 yield에 응답합니다.
class Fruit
def initialize
@kinds = %w(orange apple)
end
def kinds
yield @kinds.shift
yield @kinds.shift
end
end
f = Fruit.new
enum = f.to_enum(:kinds)
enum.next
=> "orange"
enum.next
=> "apple"
따라서 외부 반복자를 사용하여 종류를 호출 할 때마다 한 번만 yield를 호출합니다. 다음에 호출 할 때 다음 수율 등을 호출합니다.
enum_for와 관련하여 흥미로운 내용이 있습니다. 온라인 설명서에는 다음이 명시되어 있습니다.
enum_for(method = :each, *args) → enum
Creates a new Enumerator which will enumerate by calling method on obj, passing args if any.
str = "xyz"
enum = str.enum_for(:each_byte)
enum.each { |b| puts b }
# => 120
# => 121
# => 122
심볼을 enum_for의 인수로 지정하지 않으면, 루비는 열거자를 수신자의 각 메소드에 연결합니다. 일부 클래스에는 String 클래스와 같은 각 메소드가 없습니다.
str = "I like fruit"
enum = str.to_enum
enum.next
=> NoMethodError: undefined method `each' for "I like fruit":String
따라서 enum_for로 호출 된 일부 객체의 경우 열거 방법이 무엇인지 명시해야합니다.
항복 은 이름없는 블록으로 사용되어 메소드의 값을 리턴 할 수 있습니다. 다음 코드를 고려하십시오.
Def Up(anarg)
yield(anarg)
end
하나의 인수가 할당 된 "Up"메소드를 작성할 수 있습니다. 이제이 인수를 할당하여 관련 블록을 호출하고 실행할 수 있습니다. 파라미터 목록 다음에 블록을 할당 할 수 있습니다.
Up("Here is a string"){|x| x.reverse!; puts(x)}
Up 메소드가 yield와 함께 yield를 호출하면 요청을 처리하기 위해 블록 변수로 전달됩니다.