GOTO M.

趣味のコーディングとか、勉強とか、読書とか

『第7回デスマコロシアム』答案

CodeIQ『第7回デスマコロシアム』(※)に参戦しました。
※以下を特徴とするコードゴルフ大会です。

  • ideoneで使用可能な言語なら何でもOK。
  • 有利な言語に偏りすぎないよう、参戦人数の多い言語にはハンディあり。
  • バイト数でなく、文字数で競う。
  • import文は文字数カウント対象外、ideoneの自動生成部分もカウント対象外。(本稿のコードも、自動生成部分は略記しています)

第7回出題テーマは、「"deathma colosseum"を出力するbrainf**kのコード」を標準出力に出力する問題。brainf**kのコード自体は問題文で指定されているので、あまり深く考える必要は無く、要は「>("deathma colosseum"の各文字のasciiコード).」という文字列を出力するだけです。問題もシンプルなので、以下を以下に短く書けるかが短縮化のポイントとなったのかなと思います。

  • 文字列から各文字をiterateする
  • 文字をasciiコード(数値)に変換する
  • "+"を指定した回数繰り返す
  • 上記の結果を標準出力に出力する


いつもCOBOLでばかり参加していたので、今回は勉強がてら色々な言語で書いてみました。

Clojure(70)

(doseq[%"deathma colosseum"](printf">%s."(apply str(repeat(int%)'+))))
  • 普通にmap関数とかを使って書くと遅延シーケンスに苦しめられるので、doseqで書くのが良さそうです。

C#(74)

foreach(int c in"efbuinb!dpmpttfvn")Console.Write(">"+".".PadLeft(c,'+'));
  • 「"+"を指定した回数繰り返す」&「その後に"."を出力する」処理を「"."を指定桁になるよう"+"で桁揃えして出力」として一遍に行うことで、多少文字が稼げます。そのために"deathma colosseum"の文字そのものでなく、各文字をrot-1した"efbuinb!dpmpttfvn"をiterateしてやる必要があります。

F#(73)

"efbuinb!dpmpttfvn"|>Seq.iter(fun c->printf">%s"(".".PadLeft(int c,'+')))
  • 同上。文字列とかのiterateがC#よりも1文字だけ楽なのかな。

Go(72)

import . "strings"
for _,c:=range"deathma colosseum"{fmt.Printf(">%s.",Repeat("+",int(c)))}
  • 冒頭に挙げたポイントのどれもが少しずつ書き辛く、もどかしい言語です。

Groovy(48)

'deathma colosseum'.any{print">${'+'*(int)it}."}
  • 今回書いた言語では唯一、無名関数の引数を宣言しなくても"it"として参照できる点が有利そうでした。(一応Scalaもできるけど、癖が有って今回の短縮には使えなそうでした)
  • ".each"でなく".any"を使って1文字短縮するあたりがちょっとしたコツでしょうか。

Haskell(64)

import Data.Char
mapM putStr$[">"++([1..ord c]>>"+")++"."|c<-"deathma colosseum"]
  • 文字列の結合に"++"と2文字必要となるのが辛い。

Java/Java7(95)

import static java.lang.System.out;
import static java.lang.String.format;
for(int i:"efbuinb!dpmpttfvn".toCharArray())out.print(format(">%"+i+"s",'.').replace(' ','+'));
  • 「"+"を指定した回数繰り返す」が絶望的に苦手なので、なんとか被害を抑えるためにformat関数と、%数値s(空欄埋め右寄せで指定桁数にて数値を印字)、replaceを用いて、C#,F#で言うところのPadLeftを実現しています。
  • "import static"を使うことで、print系やformatを完全修飾でない形で使えるのがポイントでしょうか。デスコロのimportノーカウントルールではとても便利です。
  • Air_Hold様の回答にありましたが、"toCharArray"の代わりに"getBytes"が使えるのですね(-3文字)、不勉強でした……。

Java/Java7(91)※機種依存でCodeIQ投稿不可、ideoneでは稼働

for(int i:"𑟖𐯥𒟞𐮑𑏠𓟠𕏤𑯦n".toCharArray())out.print(format(">%"+i%200+"s",'.').replace(' ','+'));
  • サロゲートペア(UTF-16の4バイト文字)はchar2つ分として扱うことができるので、今回の問題でネックだった"deathma colosseum"の文字を短縮することが可能です。
    • ただしサロゲートペアは機種依存文字扱いとなり、CodeIQでは投稿できません。残念。
    • また、サロゲートペアからそのままアルファベットのasciiコードに相当する小さい数値を取り出すことは出来なそうなので、%200などとしてやるなどで冗長になってしまいます。三歩進んで二歩下がる感じ。

Nemerle(69)

using System.Console;
using System.Convert;
foreach(c in"deathma colosseum")Write($">$(String('+',ToInt32(c))).")
  • これもどうも、今回は活躍し辛い言語。

PHP(74)

for($s="efbuinb!dpmpttfvn";$i<17;)echo str_pad(">",ord($s[$i++]),"+").".";
  • C#,F#などと同じくpaddingを使うと短くできます。
  • foreach文を使おうとするとどうしても文字列をiteratableな形に変形する必要がありますが、PHPだとstr_splitというダダ長い関数を使う必要がありますので、いっそ使わない方が短くできそうです。
  • 文字列リテラルのダブルクォーテーションは基本的には省略可能なようですが、"!"のように英数字以外の文字が混ざると省略できなくなってしまうようで残念。

Python(59)

print"".join('>'+'+'*ord(c)+'.'for c in"deathma colosseum")
  • 末尾の改行/空白無しでのprintが結構面倒なので、printは一度に抑える形の方が楽そうです。
  • foreach的なことをするために、リスト内包表記([f(n) for n in ほげほげ])とするよりも、ジェネレータ式(f(n) for n in ほげほげ)とすることで括弧がちょっと減らせます。

Python 3(54)

for b in b"deathma colosseum":print('>'+'+'*b,end=".")
  • 末尾の改行/空白無しprintがPythonよりも短く書けます。
  • b"ほげほげ" でbyte列のリテラルを表現できるので、取り出してそのまま(ordとかintとかせずに)数値として扱えます。

Ruby(44)

"deathma colosseum".bytes{|c|$><<?>+?+*c+?.}
  • 結局このRuby(44)に逃げました。特に言うことは無いです。

Scala(46)

for(c←"deathma colosseum")print(s">${"+"*c}.")
  • rotary-o様の過去回答を見て、「←」の表現に気付きました。全角文字を演算子に使えるのは珍しいですね。

Scala(41)※QA17で撃沈 + 機種依存でCodeIQ投稿不可、ideoneでは稼働

import java.lang.System.out.{printf=>p}
for(c←"𑏕𐟤𒏝𐞐𐿟𓏟𔿣𑟥m")p(s">${"+"*(c%200)}.")
  • サロゲートペア(UTF-16の4バイト文字)をchar2つ分として扱えるのはJavaと同様です。(そしてもちろん、機種依存文字なのでCodeIQには投稿できません)
  • 別名importを仕えばかなり文字数も減るのですが、QA17(別名importも集計対象外と考えて良いか)に該当してしまうので撃沈。