『ある金額になるコインの組み合わせ』 に挑戦

お題:ある金額になるコインの組み合わせ - No Programming, No Life


Javaで「ある金額になるコインの組み合わせ」 - terazzoの日記 を Groovy に翻訳しようといろいろ調べていたら Tree iterator が簡単そうだったので実装してみた*1
参考

@Grab(group='com.google.guava', module='guava', version='r09')
import com.google.common.base.Function
import com.google.common.base.Predicate
import static com.google.common.collect.Iterators.transform
import static com.google.common.collect.Iterators.filter


def cc(amount, coins) {
  def root = [amount, coins, []]
  iterator([root] as LinkedList)
}

def iterator(stack) {
  def next = {
    def node = stack.removeLast()
    def (amount, coins, path) = node
    if (!coins.empty) {
      def (coin, rest) = [coins.head(), coins.tail()]
      // right-subtree は coin を使用しない場合
      if (amount != 0 && !rest.empty)
        stack.addLast([amount, rest, path])
      // left-subtree は coin を使用する場合
      if (amount-coin >= 0)
        stack.addLast([amount-coin, coins, [*path, coin]])
    }
    return node
  }
  def iter = [hasNext: { !stack.empty }, next: next] as Iterator
  def pred = { amount, coins, path -> amount == 0 } as Predicate
  def tran = { amount, coins, path -> path } as Function
  // node が leaf の場合に絞込み path を出力する
  transform(filter(iter, pred), tran)
}


cc(10,[1,5,10,50,100,500]).with {
  assert it.next() == [1,1,1,1,1,1,1,1,1,1]
  assert it.next() == [1,1,1,1,1,5]
  assert it.next() == [5,5]
  assert it.next() == [10]
  assert it.hasNext() == false
}

Tree iterator は stack を使い iterate する毎に stack からノードを取り出して返している。
ノードに子ノードがあれば取り出したときに stack に格納する。
今回は親ノードから子ノードを作成できるので iterate しながら tree を作成している感じになった。

*1:TODO 継続を理解する