Code Aquarium

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

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

前回投稿では標準出力周りが微妙な感じで終わってしまいました。折角なのでどうすれば出力も楽になるか考えてみました。こんなテンプレになりました。

(def in-seq #(line-seq (java.io.BufferedReader. *in*)))
(def s->long #(Long/parseLong %))
(defprotocol Out (output [x]))
(extend-protocol Out
  clojure.lang.Sequential
  (output [x] (map println x))
  Object
  (output [x] (println x)))

(defn solve [lines]
  )

;;(dorun (-> (in-seq) solve output))

sequentialオブジェクトならば改行しながら要素を出力。それ以外はprintlnで単純出力をします。
問題によっては出力の型が常に一致しているとは限らないことがあります。
例えば「解があるときは列挙して、無いときは"NG"と出力」のようなケースです。
そいういう場合でもsolveはoutputのI/Fを気にすることなく解を返せるようになりました。

注意点としては「Javaのネイティブ配列は sequentialではない」ということです。解を配列で作成した場合にはsolveから返す前にseq関数適用する必要があります。 その辺りはClojureの少し分かりにくい話題になりますが、こちらなどが参考になります。
* 翻訳:"Inside Clojure's Collection Model" - 草の根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の中に書いてしまってもよいかもしれないですね。

(ClojurClr) clojure.xml の代替品

clojure clr には何故か未だ clojure.xml が移植されていないようです。JavaのSAX API .NetXML APIがまるっきり異なっているので後回しにされている、といったところでしょうか?

代替品もなさそうなので適当に書いてみました。

https://gist.github.com/mnzk/194e226c262513d9ca1c

出力は clojure.xmlのparseコンパチにしているので clojure.zip や clojure.data.zip.xml と連携もできるのではないかと思います。*1
clojure.data.zip.xmlclojure.xml を require しているので、linq.xmlに書き換える作業は必要です。

*1:テストしていませんが