[ruby] 루비의 블록 및 수율

yield루비에서 블록과 그 작동 방식 을 이해하려고합니다 .

어떻게 yield사용 되나요? 내가 본 많은 Rails 응용 프로그램 yield은 이상한 방식으로 사용 되었습니다.

누군가 나에게 설명하거나 이해할 수있는 곳을 보여줄 수 있습니까?



답변

예, 처음에는 약간 수수께끼입니다.

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


답변

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();
    }
  }
}

전체 스트림 체인을 무시하고 아이디어는 이쪽

  1. 정리해야 할 리소스 초기화
  2. 자원 사용
  3. 그것을 청소하십시오

이것이 루비에서하는 방법입니다.

File.open("readfile.rb", "r") do |infile|
    while (line = infile.gets)
        puts "#{counter}: #{line}"
        counter = counter + 1
    end
end

전혀 다릅니다. 이걸 분해

  1. File 클래스에 리소스를 초기화하는 방법을 알려줍니다.
  2. 파일 클래스에게 무엇을해야하는지 알려주십시오
  3. 여전히 입력하고있는 자바 사람들을 비 웃으십시오. 😉

여기서 1 단계와 2 단계를 처리하는 대신 기본적으로 다른 클래스에 위임합니다. 보시다시피, 작성해야하는 코드의 양이 급격히 줄어들어 읽기가 쉬워지고 메모리 누수 나 파일 잠금이 지워지지 않을 가능성이 줄어 듭니다.

이제는 자바에서 비슷한 것을 할 수없는 것과는 달리 실제로 사람들은 수십 년 동안 그것을 해왔습니다. 이것을 전략 패턴 이라고합니다 . 차이점은 블록이 없으면 파일 예제와 같은 간단한 방법으로 작성 해야하는 클래스 및 메소드의 양으로 인해 전략이 과도하게 사용된다는 것입니다. 블록을 사용하면 그렇게 간단하고 우아한 방식으로 코드를 구성하지 않는 것이 합리적입니다.

이것은 블록이 사용되는 유일한 방법은 아니지만 다른 것 (예 : form_for api에서 레일에서 볼 수있는 빌더 패턴)은 머리를 감싸면 무슨 일이 일어나고 있는지 분명해야합니다. 블록을 볼 때 일반적으로 메소드 호출이 원하는 것으로 가정하고 블록이 원하는 방식을 설명하는 것이 안전합니다.


답변

이 기사 가 매우 유용하다는 것을 알았습니다 . 특히 다음 예제는

#!/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을 생성 하는 기능을 실행합니다 . 그런 다음 이 내부 변수 에서 함수 를 호출 하면 명령 을 실행하기위한 코드 블록이 생성 됩니다. 참고 가 요청한 파일 형식 인 경우에만 얻을 것입니다. (기술 :이 함수 는 소스 에서 볼 수있는 것처럼 실제로 사용 하지 않지만 기능은 본질적으로 동일합니다. 이 질문 은 토론을 참조하십시오 .) 이것은 함수가 초기화를 수행 한 다음 호출 코드에서 입력을 가져 오는 방법을 제공합니다. 그런 다음 필요한 경우 처리를 계속하십시오.doformat.htmlrender.htmlblock.callyield

또는 다르게 말하면 익명 함수를 인수로 사용하여 자바 스크립트에서 호출하는 함수와 비슷합니다.


답변

루비에서 블록은 기본적으로 어떤 방법 으로든 전달되고 실행될 수있는 코드입니다. 블록은 항상 메소드와 함께 사용되며 일반적으로 인수로 데이터를 공급합니다.

블록은 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 에서 더 많은 것을 보십시오


답변

때로는 다음과 같이 “수율”을 사용합니다.

def add_to_http
   "http://#{yield}"
end

puts add_to_http { "www.example.com" }
puts add_to_http { "www.victim.com"}