수색…


재귀 함수

Ruby에서 재귀를 구현하는 방법을 간단히 살펴 보겠습니다.

빵집에는 판매 할 제품이 있습니다. 제품은 팩에 있습니다. 그것은 단지 팩으로 주문을 서비스합니다. 가장 큰 팩 크기에서부터 포장이 시작되고 나머지 수량은 다음 팩 크기로 채워집니다.

예를 들어 16의 주문이 접수되면 베이커리는 5 팩 2 개와 3 개 팩 2 개를 할당합니다. 2 5 + 2 3 = 16. 이것이 재귀에서 어떻게 구현되는지 보자. 여기서 "allocate"는 재귀 함수입니다.

#!/usr/bin/ruby

class Bakery
  attr_accessor :selected_packs

  def initialize
    @packs = [5,3] # pack sizes 5 and 3
    @selected_packs = []
  end

  def allocate(qty)
    remaining_qty = nil

    # ==============================================
    # packs are allocated in large packs first order
    # to minimize the packaging space
    # ==============================================
    @packs.each do |pack|
      remaining_qty = qty - pack

      if remaining_qty > 0
        ret_val = allocate(remaining_qty)
        if ret_val == 0
          @selected_packs << pack
          remaining_qty = 0
          break
        end
      elsif remaining_qty == 0
        @selected_packs << pack
        break
      end
    end

    remaining_qty
  end
end

bakery = Bakery.new
bakery.allocate(16)
puts "Pack combination is: #{bakery.selected_packs.inspect}"

출력은 다음과 같습니다.

팩 조합은 다음과 같습니다 : [3, 3, 5, 5]

테일 재귀

많은 재귀 알고리즘은 반복을 사용하여 표현할 수 있습니다. 예를 들어, 가장 큰 공통 분모 함수는 재귀 적 으로 작성할 수 있습니다.

def gdc (x, y)
  return x if y == 0
  return gdc(y, x%y)
end

또는 반복적으로 :

def gdc_iter (x, y)
  while y != 0 do
    x, y = y, x%y
  end

  return x
end

두 알고리즘은 이론적으로는 동일하지만 재귀 버전은 SystemStackError를 위험에 빠뜨 립니다. 그러나 재귀 적 메서드는 자체 호출로 끝나기 때문에 스택 오버플로를 피하기 위해 최적화 할 수 있습니다. 또 다른 방법은 그것을 넣어 : 컴파일러가 메소드의 끝에서 재귀 메서드 호출을 찾기 위해 알고있는 경우 재귀 알고리즘은 반복과 같은 기계 코드가 발생할 수 있습니다. 루비는 기본적으로 꼬리를 호출 최적화를하지 않습니다,하지만 당신은 할 수 와 함께 전원을 켭니다 :

RubyVM::InstructionSequence.compile_option = {
  tailcall_optimization: true,
  trace_instruction: false
}

꼬리 - 호출 최적화 기능을 켜는 것 외에도 명령 추적 기능을 해제해야합니다. 아쉽게도 이러한 옵션은 컴파일시에만 적용되므로 다른 파일에서 재귀 메서드를 require 하거나 메서드 정의를 eval 해야합니다.

RubyVM::InstructionSequence.new(<<-EOF).eval
  def me_myself_and_i
    me_myself_and_i
  end
EOF
me_myself_and_i # Infinite loop, not stack overflow

마지막으로 최종 반환 호출은 메서드와 메서드 반환해야합니다. 즉, 표준 계승 함수를 다시 작성해야합니다.

def fact(x)
  return 1 if x <= 1
  return x*fact(x-1)
end

무엇인가 :

 def fact(x, acc=1)
   return acc if x <= 1
   return fact(x-1, x*acc)
 end

이 버전은 누적 합계를 기본값 인 1 (선택 사항) 인수를 통해 전달합니다.

추가 읽기 : RubyTailin 'Ruby의 테일 호출 최적화 .



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow