読者です 読者をやめる 読者になる 読者になる

Code Aquarium

minazoko's blog -*- 水底のブログ -*-

(Clojure) 標準入力 - オンライン判定コード提出用テンプレ

Clojure

最近PaizaのオンラインハッカソンやCodeIQの出題コードを問いたりして遊んでいます。 この手のサイトでは標準入出力を使います。 出力はともかく標準入力読み取りを毎回考えて書くのは無駄なのでコピペできるテンプレを用意するのが常套手段のようです。

ところで、このページのClojureコード例を見てくれないか?
* 各言語の標準入出力サンプル|CodeIQ│CodeIQ
なんともムズムズするサンプルですね。

なぜコードが真っ赤なのか、は置いとくとして、ClojureはLazySequenceがお手軽に扱えるのが売りですから標準入力もシーケンスとして取得しておいたがよいでしょう。 それからuseは今となっては非推奨なオペレータなのでrequireにしましょう。

というわけで、自分ならこう書きます。お題は「すべての入力をupperして出力」です。

(def in-seq #(->> (repeatedly read-line)
                  (take-while identity)))

(require '[clojure.string :refer :all])

(dorun (->> (in-seq)
            (map #(upper-case %))
            (map println)))

repeatedly だけだと無限にreadしてしまうので、読み取り継続判定(終了判定)を付け加えています。 read-lineはEOFでnilを返すのでidentityで判定すれば「偽」で止まります。

通常clojureのコードでは一行目にネームスペース宣言があり、その後にモジュールrequire等が並びますが、今回の用途ではネームスペースは不要なので省略しました。 in-seq よりもrequireが下にあるのは書き換え頻度が低いものを上方にまとめるという方針。

サンプルの書き換えとしてはこんなものかと思いますが、実際に活用するとなるともう少し手を入れたくなります。 現時点では次のようなテンプレを使いまわしています。

(def in-seq #(line-seq (java.io.BufferedReader. *in*)))
(def s->long #(Long/parseLong %))

(defn solve [lines]
  )

;;(dorun (->> (in-seq) solve (map println)))

s->longを追加してあります。よく使う関数はこのように上の方にまとめていきます。
最後の行も定型文です。コメントアウトしてあるのは、emacsで間違ってバッファを評価してコードが実行され無限ループでフリーズみたいな状況を避けるため。提出時にはコメントを外します。

line-seqはjava.io.BufferedRederを受け取り行毎のlazySeqを返します。EOFに到達するとそこで読み取り停止してくれるのでこちらの方が少し楽です。

またはこちらのようにclojure.java.io/readerを使ってもよいですね。Javaクラスを剥き出しで使いたくなければどうぞ。
* 各プログラミング言語の標準入出力サンプル(ややマイナーなもの用) - Qiita

あとは、問題に応じてsolve関数を完成させるだけです。

追記

コメントアウトの最終行は出力が単一行だと面倒なことになりますね。 出力が一行の場合は。(map println) を println に書き換えるなりしないとダメでした。
実際どうしているのかというと、コメントアウトは2行あって出力によって使う方を選択していました。テンプレ化せずに出力はsolveの中に書いてしまってもよいかもしれないですね。