CodeIQ:shoheyコマンド

私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「shoheyコマンド」(CodeIQ)を参照してください。

問題の概要

問題を引用します。
shoheyコマンドを実装してください。

【仕様】
  • 標準入力から以下の様なフォーマットでコマンドを受け付けます(各項目は半角スペース区切りです)
  • コマンドの区切り文字は、半角スペース1文字とします(連続するスペースは入力として与えられない)
    shohey 2 -e -q
  • shoheyはコマンド名です。必須で指定されます
  • shoheyコマンドは数値引数と -e , -q のオプションを受け付けます
  • 数値引数は必須で指定されます
  • 数値引数はshoheyコマンドの第1引数として指定されます
  • 数値引数には 1-10 の整数が指定されます
  • 数値引数で指定した回数分 sho のあとの hey を繰り返し出力します
    例1: shohey 2 => shoheyhey
    例2: shohey 3 => shoheyheyhey
  • -eオプションは任意で指定されます。数値引数以降に指定されます。
  • -eオプションが指定された場合は数値引数で乗算されたheyをさらに2倍の個数出力します
    例1: shohey 2 => shoheyhey
    例2: shohey 2 -e => shoheyheyheyhey
  • -qオプションは任意で指定されます。数値引数以降に指定されます。
  • -qオプションを指定すると最後のheyの後に半角クエスチョンマーク(?)がつきます
    例1: shohey 2 => shoheyhey
    例2: shohey 2 -q => shoheyhey?
  • -eオプションと-qオプションは順不同です
    例1: shohey 2 -e -q => shoheyheyheyhey?
    例2: shohey 2 -q -e => shoheyheyheyhey?

私のプログラム

Rubyで解答しています。

#!/usr/bin/ruby

def shohey(args)
	num = args.shift.to_i
	q = ""

	for o in args
		case o
		when "-e" then
			num *= 2
		when "-q" then
			q = "?"
		end
	end

	puts "sho" + "hey" * num + q
end

while line = gets
	line.strip!
	if line.empty? then
		next
	end

	args = line.split(" ")
	cmd = args.shift
	send(cmd, args)
end

解説

問題自体は簡単です。
なのでちょっと変わったやり方(と言うほどではありませんが)をしてみました。

send()

27行目です。この行でshohey()を呼んでいるのですが、ちょっと変わっていると思います。send()は第一引数に指定した関数に第二引数に指定した配列を第一引数の関数の引数として呼び出す関数です。
問題で与えられるコマンドはshoheyだけなので別にこんなことをしなくても良いのですが、2以上のコマンドがあった場合に拡張可能なように工夫してみました。

shohey()

プログラムの本体です。
一つ目の引数は数値と決まっているのでnumに取ります。
その後のオプションは順不同なのでループで取り出します。最初C言語のgetopt()みたいなものを探そうと思いましたが、面倒になってやめてしまいました。ループと条件分岐を組み合わせるのは順不同のオプションのようなものを実現するための常套手段だと思います。
仕様通り、-eならnumを2倍、-qなら最後に?をつけるようにします。変数qは-qが指定されていなければ空文字列になっているので出力時に条件判断して処理を分ける必要がありません。

雑感

問題は簡単ですが、割と実務でもやりそうな感じです。実際、このようにオプションをとるコマンドは何回か実装したことがあり、その時の記憶でC言語にgetopt()と云う関数があることを知っていました。
ちなみに、rubyで任意の関数を呼び出す関数がsend(func, args)なのはオブジェクト指向で言うところのメッセージパッシングのイメージだからでしょうか?