CodeIQ MAGAZINECodeIQ MAGAZINE

汚コードグランプリ!秀逸な汚コードにまみれた解答臭をご覧あれ #逆リファクタリング

2015.02.13 Category:CodeIQ問題解説・リーダーボード Tag:

  • 2296
  • このエントリーをはてなブックマークに追加
20150213_tbpgr_e

一部から「逆リファクタリング!?」とざわつきが聞こえた
「コードを汚く逆リファクタリング問題」の優秀解答発表です!

どんなコードが送られてきたでしょうか?
by tbpgr

はじめに

出題者のtbpgrです。
当記事では「コードを汚く逆リファクタリング」問題の優秀解答発表を行います。

  1. 出題内容について
  2. 優秀解答紹介

出題内容について

問題文

リファクタリングは外部から見た振る舞いを変えずに内部構造の保守性、可読性、拡張性などを改良する手法です。
今回は逆に、外部から見た目の振る舞いを変えずに、内部構造をあなたの考える汚いコードに逆リファクタリングしてください。

・リファクタリング前 対象プログラム

print gets.upcase.reverse.split('').join(' ')

リファクタリング前のプログラムは Ruby ですが、以下の制約を満たしていれば、リファクタリング後のプログラムはRuby以外でも構いません。
提出可能な言語は、ideoneで動作確認可能な言語です。

  • 制約1: 標準入力「CodeIQ」に対して、「Q I E D O C」を標準出力すれば正解とします。
    ※出力文字列の各文字の間には半角スペースがあります
  • 制約2: ideoneで実行不可能な場合は不正解とします。提出コードをそのままコピペすれば実行可能な状態にしてください。
  • 制約3: プログラムは1,000文字以下にしてください。提出コードをRubyのsizeメソッドでチェックします。

出題意図

汚ないコードを綺麗なコードに直すには、何が汚いコードなのかを明確に把握している必要があります。
そこで、あえて汚いコードを書く問題を作成すれば、各自が経験してきたバッドプラクティスがコードに現れて面白いかな、というところが出題のきっかけでした。

その点を明確にしてもらう意味でも、解答フォーマットにポイントを列挙する形にしました。

上記のように真面目な理由のある「汚いコード」だけではなく、曲芸的な解答も想定され、1問で二度美味しいCodeIQ MAGAZINE記事ができあがりそうだ、
という目論見もありました。

優秀解答紹介

コードの臭い編

オーソドックスに、何かしらのコードの臭いにあたるポイントを元に汚いコードを書いている解答群を紹介します。

ttakuru88 様

◆タイトル
見た目が汚い

◆使用言語
Ruby

◆プログラム

# encoding: UTF-8

(σ・∀・)σゲッツ!!=gets
(;_;)=''

i = 0
loop do
  if i>=(σ・∀・)σゲッツ!!.size
      break
    end
  コード=(σ・∀・)σゲッツ!![i].ord
  if コード==10
      break
    elsif i>0
        (;_;)=(;_;)+32.chr
  end
  if コード>=97
        if コード<123
            (σ・∀・)σゲッツ!![i]=(コード-32).chr
  end
    end
  (;_;)=(;_;)+(σ・∀・)σゲッツ!![i]
    i+=1
end

i=(;_;).size
loop do
if i< 1
break
else
print (;_;)[i-1]
i-=1
end
end

◆ポイント

  • 遊び心で変な変数名を使う
  • スペースとタブの混在
  • インデントしない、深さが合っていない
  • 演算子前後を詰める
  • 無駄にifをネストする
  • 無駄にローレベルなテクニックを使う
  • マジックナンバー

■出題者コメント
コードの臭いや体裁の汚さなどのポイントを挙げつつ、
顔文字での遊び心に癒やされる汚コード。

せきゅあ 様

◆タイトル
何やってるかよくわかラムダった(Д

◆使用言語
Ruby

◆プログラム

d=lambda do |b, c|
 case b
when 2 then
 b=gets.chomp.split(//)
  when 3 then
 b=[]
  c.length.times do |f|
 if c[f].ord>=97 && c[f].ord<=0x7a
  b << (c[f].ord-32).chr
 else
b << c[f]
 end
end
 when 7 then
b=c.reverse!
 end
b
 end
puts d.call(7,d.call(3,d.call(2,2))).join(' ')

◆ポイント

  • 何でも屋なラムダ
  • 無意味にラムダ
  • マジックナンバー(case 文の数値 特に意味なし)
  • 謎の引数
  • if文の10進、16進混在
  • 一時変数の再利用
  • 車輪の再開発(全部大文字)
  • インデント不揃い
  • 適当な変数名abcdef
  • 無駄な副作用メソッドの使用reverse!

■出題者コメント
出題意図である、コードの臭いを多数列挙していただいた解答。

tomwot 様

◆タイトル
余計なことしなくてよいのに

◆使用言語
Ruby

◆プログラム

$strInput = ARGF.gets
$arrInput = $strInput.scan(/(.)/)

$arrInput.each_with_index{|arrA, intI|
Array['C'] == arrA ? $arrInput[0] = "Q" : Array[?d] == arrA ? $arrInput[2] = 'e' : Array["e"] == arrA ? $arrInput[3] = ?d : Array['I'] == arrA ? $arrInput[4] = "o" : Array[?o] == arrA ? $arrInput[1] = 'I' : Array["Q"] == arrA ? $arrInput[5] = ?C : nil
}

$strTmp = ""
$arrInput.each_with_index{|strI, intI|
$strTmp = $strTmp + strI[-1].capitalize + " "
}
$arrReturn = $strTmp.scan(/(.)/)
$arrReturn[11] = ""

puts $arrReturn.join("")

◆ポイント

  • インデントなし。
  • 無意味なグローバル変数。
  • システムハンガリアン。
  • ARGF.getsはgetsだけで良い。
  • 一文字ずつ切り出すのが目的であればString#scanの引数の括弧は無意味。
  • each_with_indexのブロックパラメータのインデックスを使っていない(のであればeachで十分)。
  • 三項演算子の連打。
  • 三項演算子の順番が意味不明(左辺の大文字小文字を無視した昇順)。
  • 無意味なArray[]。
  • レシーバをブロック内で書き換えしている。
  • 文字列リテラルの記述が不統一(’a’, “a”, ?a)。
  • 一文字しかないことが明らかな文字列にインデックスを指定している。しかも-1。
  • 文字列を大文字にするのに一文字しかないからといってString#capitalizeを使っている。
  • Array#joinをコーディングしている。
  • +=を使っていない。
  • 上(8〜13行目)ではArray#joinを使っていないのに下(15行目)では使っている。でも引数が空文字列なら引数を省略可。
  • 入力が”CodeIQ”以外の場合に、想定通りに動作することが期待できない。

■出題者コメント
こちらも出題意図である、コードの臭いを多数列挙していただいた解答。
Ruby観点のものと、言語非依存の観点がありますね。

frost 様

◆タイトル
ループ停止は例外スルーで

◆使用言語
Java

◆プログラム

import java.util.*;import java.lang.*;import java.io.*;class Ideone{public static void main (String[] args) throws java.lang.Exception{int a;java.util.ArrayList<Integer> b = new java.util.ArrayList<java.lang.Integer>();while(true){if((a=System.in.read())==-1)break;if(97<=a && a<122)a=a-32;b.add(a);}a=0;try{while(true){System.out.print((char)((int)b.get(b.size()-1-a)));if(a<b.size()-1)System.out.print(" ");a++;}}catch(java.lang.Exception c){}}}

◆ポイント

  • 可読性を下げるために改行はすべて削除
  • 変数名はa,b,cで意味を持たせない
  • 変数aを前半のループと後半のループで使いまわす
  • クラス名はパッケージ名も含めてフルで指定
  • 小文字かどうかの判定もアスキーコードで判断
  • 大文字にするときもメソッドを使わずにアスキーコードに32を加算することで大文字化
  • 繰り返しはwhile(true)で行う
  • 2つめの繰り返しに関しては内部でListへの要素アクセスをおこなっているので、breakも行わず、
    IndexOutOfBoundsExceptionをcatchするだけして、スルーすることでループを終了させながら見かけ上正常動作しているように見せている

■出題者コメント
こちらもまた出題意図である、コードの臭いを多数列挙していただいた解答。
例外をロジックに活用してしまったり、変数の使い回しなどは、実際に実務で何度も見かけたことがあって共感します。

oda1979 様

◆タイトル
中途半端に車輪の再発明

◆使用言語
Ruby

◆プログラム

def my_upcase(s)
  t = ""
  s.size.times do |i|
    t[i] = (s[i].ord >= ?a.ord && s[i].ord <= ?z.ord) ? (s[i].ord-32).chr : t[i] = s[i]
  end
  t
end

def my_reverse(s)
  t=""
  s.size.times do |i|
    t[i] = s[s.size-1-i]
  end
  t
end

# 手抜きなので1文字ずつしか分割できない
def my_split(s)
  t = []
  s.size.times do |i|
    t << s[i]
  end
  t
end

def my_join(a, join_string)
  t = ""
  a.size.times do |i|
    t += a[i]
    t += join_string if i != a.size-1
  end
  t
end

# function内でgetsしちゃうのでわかりにくい
def my_gets_upcase_reverse_split_join(join_string)
  my_join( my_split(my_reverse(my_upcase(gets))), join_string)
end

print my_gets_upcase_reverse_split_join(' ')

◆ポイント

  • 車輪の再発明を心がけました。
  • eachではなく「size.times」を使うことであまりRubyっぽくなくなりました。(forのほうがもっと良かったかもしれません。)
  • splitは再発明に挫けていて、中途半端な実装で使うのに制限事項があります。
  • my_gets_upcase_reverse_split_joinはいらないけど作りました。

■出題者コメント
車輪の再発明については実際に現場でもよくありますね。

  • ライブラリの知識
  • こういったライブラリはすでに存在しているだろう、と嗅ぎ分ける力

などの重要性が詰まったテーマのため掲載対象に選びました。

th 様

◆タイトル
ぐるぐる再帰

◆使用言語
Node.js

◆プログラム

/*jslint node: true*/

'use strict';

function each(a, b) {
    b(a.shift());
    if (a.length) {
        each(a, b);
    }
}

var a = function (a) {
    var A = arguments.length + 1,
        B = Math.pow(A, 5),
        C = B * A + Math.ceil(A / B);
    each((function f(a, b, c) {
        var d = a.length, e = B;
        if (b) {
            d -= A / 2;
            e = a.charCodeAt(d);
            a = a.slice(0, d);
            if (e < C) {
                f(a, b, c);
                return c;
            }
            e = (e - C) % B + C;
        } else {
            if (!d) {
                c.push(A * 5);
                return c;
            }
        }
        c.push(e);
        return f(a, !b, c);
    }(a, a, [])), function (a) {
        process.stdout.write(String.fromCharCode(a));
    });
};

each([
    ['resume'],
    ['setEncoding', 'utf8'],
    ['on', 'data', a]
], function (a) {
    process.stdin[a[0]](a[1], a[2]);
});

◆ポイント

  • 無駄に再帰使ってます。
  • 計算も無駄だらけです。
  • JSLint(コードチェッカ)に合格するのに残念なコードです。

■出題者コメント
「 JSLint(コードチェッカ)に合格するのに」という部分が掲載の決め手でした。

なおゆら 様

◆タイトル
これでお代官様もイチコロ(はーと)ツッコミどころ満載!悪代官接待スクリプト

◆使用言語
Ruby

◆プログラム

#coding:utf-8


# 入力をチェックするよ☆(ゝω・)vキャヒ゜ by デコメ is 女子力(はーと)
#   _|_|_|                  _|            _|_|_|    _|_|      
# _|          _|_|      _|_|_|    _|_|      _|    _|    _|    
# _|        _|    _|  _|    _|  _|_|_|_|    _|    _|  _|_|    
# _|        _|    _|  _|    _|  _|          _|    _|    _|    
#   _|_|_|    _|_|      _|_|_|    _|_|_|  _|_|_|    _|_|  _|  
def isCodeIQ?(input)
  return input=='CodeIQ'
end

def char_array_to_string(arr)
  arr.join('')
end

def insert_space_between_each_character(string)
  sp = ' '
  string.split("").join(sp)
end

def upperCase(str)
  str.tr('a-z', 'A-Z')
end

class String
  # hantai なのに hentai に間違える人大杉なんなの(怒)
  def method_missing(name)
    if name.to_s=='hentai'
     self.hantai
    end
  end

  def hantai
    ret=[]
    6.times{|i|ret<<self[-1-i]}
    ret
  end
end

inputo = gets
if isCodeIQ?(inputo)
  print insert_space_between_each_character(char_array_to_string(upperCase(inputo).hentai))
else
  print "wrong input"
end

◆ポイント

  • デコメ(フォント等によっては文字が崩れて読めない)(isCodeIQ?)
  • isCodeIQ?みたいなバリデーション本当にいる?
  • 統一感のないメソッドの命名(英単語とローマ字日本語の混在)
  • キャメルケースとスネークケースの混在
  • Rubyに既にあるメソッドを用いない独自定義(upperCase)
  • メソッドの戻り値が「文字の配列」と「文字列」のものが混在
  • なぜか1メソッドがけオープンクラスに定義追加
  • 安易なmethod_missingの使用
  • String#hantaiで6(=’CodeIQ’.size)というマジックナンバーの使用
  • String#method_missingのコメントがただの不満(感想)

■出題者コメント
コードの臭いが散りばめられ、かつ小ネタ満載です。
日本語のローマ字命名の変数は実務でもありがちな例です。

難読編

読みにくいコード、読めないコード などの特徴を持つ解答群を紹介します。

izumori 様

◆タイトル
λλλλλ…

◆使用言語
Ruby

◆プログラム

lambda {|s|
  lambda {|f, s|
    s.length>1 ? lambda{f.call(f,s[1..-1]);print lambda{|s|" "+s}.call(lambda {|s|s.upcase}.call(s[0]))}.call : lambda{print(lambda{|s|s.upcase}.call(s))}.call
  }.instance_eval{self.curry.call(self)}.call(s)
}.call(gets.chomp)

◆ポイント

  • ラムダまみれにしてやりました
  • パッと見て気分が悪くなるコードを目指しました

■出題者コメント
「ラムダまみれにしてやりました」というポイントの、 「まみれ」 という言葉のチョイスのよさに反応しての掲載です。

Azicore 様

■出題者コメント
全国6000万のAzicoreファンの皆様 、お待たせいたしました。
生粋のBrainf*ck使いであるAzicore様の解答です。

※ Azicore様を知らない方への補足。Azicore様はCodeIQの問題をBrainf*ck で解くことが多い生粋の○○プログラマさんです。

Brainf*ck はそもそも難読言語のため、普通に書いても汚いコードになるので提出はないかと思っていました。
しかし、Azicore様クラスになると普通に書かれた Brainf*ck は綺麗なコードなのでしょう。

◆タイトル
言語の選定ミス

◆使用言語
Brainf**k

◆プログラム

>,+[-[>+<<+>-]>>>++++++++[<++++++++++++>-]>+<<<[->-[<]>]>[-<<<[-]++++[<++++++++>-]<[<->-]>>>>>>]<<<[+]<<++++[<++++++++>-]>,+]<<<[.<]

◆ポイント

  • 標準入力を行単位ではなく、1文字単位で読み込んでいる。
  • 大文字に変換する処理をわざわざ自前で実装している。
  • 大文字小文字の判定に必要な関数や演算子がないため相当苦労した。
  • アルファベット以外の入力に対しては不正な動作をすることがある。
  • 定数や定文字列をリテラル表記ではなく、わざわざ1ずつ足すことによって生成している。
  • 配列をスペースで結合するのではなく、文字とスペースを交互に代入した配列を作っている。
  • インデントや改行によってコードが整形されていない。
  • ideoneでは動作するが、他の多くのインタープリタでは動作せず、環境依存のコードになっている。※
  • そもそも目的の処理を行なうのにふさわしいプログラミング言語ではな(ry

※に関しての補足:
標準入力の終端は0として扱われることが多いのですが、ideoneでは-1になるようです。
標準入力の終端を0として扱う処理系では、以下のコードで同等の動作となります。

>,[[>+<<+>-]>>>++++++++[<++++++++++++>-]>+<<<[->-[<]>]>[-<<<[-]++++[<++++++++>-]<[<->-]>>>>>>]<<<[+]<<++++[<++++++++>-]>,]<<<[.<]

NeoCat 様

◆タイトル
ソースコード残ってないんだよねー

◆使用言語
C

◆プログラム

#include <sys/mman.h>
#include <stdlib.h>
__attribute__((aligned(4096))) char bin[] = "U\266\326DM\260U\244$$$U\277\355\2\
05\251\344\355\243\351(X\233\356eX\005\026`HD\354eU\244$$$$U\277'\355\276\251\3\
44\347\233e\020w\016DU\244$$$$U\277'\351h@\355\276\251\344<+\021\256\347\240D\2\
47", *i = bin;
__attribute__((constructor)) void f()
{
        while (*i) *i++^=100;
        mprotect(bin, 1, 7);
        ((void(*)())bin)();
        exit(0);
}
int main = 1;

◆ポイント

  • いやー、大元のソースコード残ってないんだよねー。
  • main関数?なにそれ?
  • ま、古い32bitマシンではちゃんと動いてるし、あとはよろしくねー。

■出題者コメント
既に退職したアーキテクトが作成した難読化されたプログラムの内容を想像しながら、
同じ機能を持つプログラムを実装する、という業務を担当した過去を思い出しました。
前任者がネットのサンプルコードを丸コピしていてくれたおかげで、内容を容易に推測できた、
というオチがつきました。

SOMETHING COOL 様

◆タイトル
コードをさらしたくない

◆使用言語
python(2.x系)

◆プログラム

import base64 as _
# s = 'print " ".join((raw_input().upper())[::-1])'
# x = " ".join(_.b32encode(s)[::-1])
# print x
x = "= = = S S O F G N R O U T N F F J B K E X S B O Q L 5 4 S U " + \
    "A F U L 5 A H X F N 7 2 5 C G Z B F I R 3 S W X J N O R I A " + \
    "C R A E U T 3 S G Z B O"
exec(_.b32decode("".join(x.split(" "))[::-1]))

◆ポイント

  • 私はソースコードを相手先に送る場合、BASE64に変換してかつそのまま実行できるようによく使っています。
  • 今回は洒落っ気を出して、お題と同じ変換にしました。但し、upperはbase32で始めから大文字です。
  • コメントアウトした部分で変換した結果をx=に貼り付けました。

■出題者コメント
ただ難読化するだけではなく、洒落っ気を出していただいた点で掲載を決定しました。

ciel 様

◆タイトル
二重の難読化

◆使用言語
Ruby

◆プログラム

eval "cmVxdWlyZSdmaWRkbGUnO3JlcXVpcmUnZGwnO2NsYXNzIFJ1YnlWTTo6SW5zdHJ1Y3Rpb25TZXF1ZW5jZTtmbj1GaWRkbGU6OkZ1bmN0aW9uLm5ldyhETDo6SGFuZGxlOjpERUZBVUxUWydyYl9pc2VxX2xvYWQnXSxbRmlkZGxlOjpUWVBFX1ZPSURQXSozLEZpZGRsZTo6VFlQRV9WT0lEUCk7ZGVmaW5lX3NpbmdsZXRvbl9tZXRob2QoOmxvYWQpe3wqYXxmbi4oKmEubWFwKCZETC5tZXRob2QoOmRsd3JhcCkpKS50b192YWx1ZX1lbmQ7UnVieVZNOjpJbnN0cnVjdGlvblNlcXVlbmNlLmxvYWQoTWFyc2hhbC5sb2FkKCcECFsTIi1ZQVJWSW5zdHJ1Y3Rpb25TZXF1ZW5jZS9TaW1wbGVEYXRhRm9ybWF0aQZpB2kGewg6DWFyZ19zaXplaQA6D2xvY2FsX3NpemVpBjoOc3RhY2tfbWF4aQgiDzxjb21waWxlZD4iDzxjb21waWxlZD4waQY6CHRvcFsAaQBbAFsSaQZbBzoKdHJhY2VpBlsGOgxwdXRzZWxmWwY7ClsLOglzZW5kOglnZXRzaQAwaR1pAFsLOws6C3VwY2FzZWkAMGkAaQZbCzsLOgxyZXZlcnNlaQAwaQBpB1sLOws6DmVhY2hfY2hhcmkAMGkAaQhbCzsLOgl0b19haQAwaQBpCVsHOg5wdXRzdHJpbmdJIgYgBjoGRUZbCzsLOglqb2luaQYwaQBpClsLOws6CXB1dHNpBjBpDWkLWwY6CmxlYXZlJyksbmlsLG5pbCkuZXZhbA==".unpack("m")[0]

◆ポイント

  • まず、Base64をデコードすると、次のようになります(可読性のため整形しています)。
require 'fiddle'
require 'dl'
class RubyVM::InstructionSequence
  fn=Fiddle::Function.new(DL::Handle::DEFAULT['rb_iseq_load'],[Fiddle::TYPE_VOIDP]*3,Fiddle::TYPE_VOIDP)
  define_singleton_method(:load){|*a|fn.(*a.map(&DL.method(:dlwrap))).to_value}
end
RubyVM::InstructionSequence.load(Marshal.load("\x04\b[\x13\"-YARVInstructionSequence/SimpleDataFormati\x06i\ai\x06{\b:\rarg_sizei\x00:\x0Flocal_sizei\x06:\x0Estack_maxi\b\"\x0F<compiled>\"\x0F<compiled>0i\x06:\btop[\x00i\x00[\x00[\x12i\x06[\a:\ntracei\x06[\x06:\fputself[\x06;\n[\v:\tsend:\tgetsi\x000i\x1Di\x00[\v;\v:\vupcasei\x000i\x00i\x06[\v;\v:\freversei\x000i\x00i\a[\v;\v:\x0Eeach_chari\x000i\x00i\b[\v;\v:\tto_ai\x000i\x00i\t[\a:\x0EputstringI\"\x06 \x06:\x06EF[\v;\v:\tjoini\x060i\x00i\n[\v;\v:\tputsi\x060i\ri\v[\x06:\nleave"),nil,nil).eval

最終行にある文字列はRuby(YARV)のバイトコードです。
これはRubyVM::InstructionSequence.compile()で得ることができます。librubyにはバイトコードを読み出す機能もあるのですが、SEGVを容易に引き起こしてしまうのでRuby側には公開されていません。そこで、fiddle/dlを用いてこの機能にアクセスします。

バイトコードとプログラム全体で二重の難読化というわけです。

提出プログラムは以下のプログラムで生成しました。

puts 'eval "'+[("require'fiddle';require'dl';class RubyVM::InstructionSequence;fn=Fiddle::Function.new(DL::Handle::DEFAULT['rb_iseq_load'],[Fiddle::TYPE_VOIDP]*3,Fiddle::TYPE_VOIDP);define_singleton_method(:load){|*a|fn.(*a.map(&DL.method(:dlwrap))).to_value}end;RubyVM::InstructionSequence.load(Marshal.load('__CODE__'),nil,nil).eval".sub('__CODE__',Marshal.dump(RubyVM::InstructionSequence.compile(DATA.read).to_a)))].pack('m0')+'".unpack("m")[0]'
__END__
puts gets.upcase.reverse.each_char.to_a.join ' '

なお、Rubyのバージョンが違うと動作しないので注意が必要です(現在のideoneは1.9.3)。

■出題者コメント
この解答の内容を確認しようとして、 るりま のドキュメントを参照していたらデッドリンクを発見して、Issue を作成することになりました。
※るりま = Ruby リファレンスマニュアル

Ruby 2.2.0 の library fiddle の dl へのリンクがデッドリンク

みけCAT 様

◆タイトル
ハフマン符号

◆使用言語
C99 strict

◆プログラム

#define xxxxx *
#define xX (
#define XXxX int
#define xxxxX 123
#define Xxxxx putchar
#define XxxX <
#define XxxxX ||
#define XXXXXx {
#define xxXxxX 96
#define xxXxXX return
#define XXXXXX }
#define XXXxx getchar
#define xxXxXx =
#define xxXX ;
#define XxX )
#define xxxX 32
#define XXXXx void
#define XXXxX main
#define xxXxxx -=

XXxX XXXxx xX XXXXx XxX xxXX XXxX Xxxxx xX XXxX XxX xxXX XXxX XXXxX xX XXXXx
XxX XXXXXx XXxX XXxx xxXxXx XXXxx xX XxX xxXX xxXxXX xX XXxx xxXxxx xxxX
xxxxx xX xxXxxX XxxX XXxx XxX xxxxx xX XXxx XxxX xxxxX XxX XxX XxxX xxxX
XxxxX xxxxX XxxX xX XXXxX xX XxX XxxxX Xxxxx xX xxxX XxX XxX XxxxX Xxxxx xX
XXxx XxX XxxX xxxX xxXX XXXXXX

◆ポイント

  • プログラムをトークン単位でハフマン符号化しました
  • コンパイルしても警告は出ません
  • トークンの種類数もなるべく減らしました
  • 入力の最初の文字が半角英字であり、かつ入力が十分短いならば、正常終了します

■出題者コメント

私から述べることは特にないのでじっくりとご覧あれ

独創性編

えちごやえちぜん 様

◆タイトル
まるなげ

◆使用言語
VB.NET

◆プログラム

    Sub Main()

        Dim sIn As String = Console.ReadLine()
        Select Case sIn
            Case "CodeIQ"
                Console.WriteLine("Q I E D O C")
            Case Else
                'Todo その他の場合を実装 → 田中君宜しくbyえちごや
        End Select

    End Sub

◆ポイント

  • 対象プログラムが理解できず、制約条件だけ読んで実装したらしい。
  • しかも例外処理が面倒になって、途中で部下の田中君(仮名)に丸投げしている。

■出題者コメント
コードというよりも、現場が・上司が汚い、という 1レイヤー上の汚さ を提供してくれた解答。

実務で「Mr まるなげ」的なリーダーを見たことがあり、ノスタルジックかつ不快な気分を味わうことができました。

naoki_kp 様

◆タイトル
手抜き

◆使用言語
Ruby

◆プログラム

print gets.upcase.reverse.split('').join(' ')

◆ポイント
* リファクタリング前のコードから変更しなければいけない制約は書いていませんね。

■出題者コメント
問題文のリファクタリング前のままのコード。
確かに、制約は書いてません。

コードの汚さよりも根性の汚さで勝負 、ということですね。
※本気で煽っているわけではありません

冬椿 様

◆タイトル
ソートによる挿入とリバース

◆使用言語
C++14

◆プログラム

#include<iostream>
#include<utility>
#include<vector>
#include<cctype>
#include<algorithm>
int main()
{
    std::string in;
    std::cin>>in;
    std::vector<std::pair<char,int>> list;
    for(int i=0;i<in.size();++i)
        list.emplace_back(std::toupper(in[i]),i);
    for(int i=1;i<in.size();++i)
        list.emplace_back(' ',i);
    std::stable_sort(list.begin(),list.end(),[](auto l,auto r){return r.second<l.second;});

    for(auto x:list)
        std::cout<<x.first;
}

◆ポイント
・ソートによってリバース処理と空白を挟み込む処理を行う

■出題者コメント
シンプルながら、パッと見て本来の目的を連想しにくい分かりにくいプログラムという内容だったので掲載を決定。

たまにいますよね、妙なロジックで実装する人。

%20 様

◆タイトル
ゲシュタルト崩壊

◆使用言語
C

◆プログラム

#define Co *
#define de &
#define I ~
#define Q -
#define C )
#define ode +=
#define CodeIQ Q 32
IQ;Code[CodeIQ Co CodeIQ];main(C{
for(;Q I(Co(Co de IQ+de Co Code C ode
getchar(C C;Co de Co(IQ+++Code C ode Q
(CodeIQ C C Co(IQ+++Code C+CodeIQ<=CodeIQ
Co I Q(CodeIQ+I CodeIQ C||(Co(Code+I Q
IQ C ode CodeIQ C;for(IQ ode I CodeIQ
+CodeIQ;IQ--;putchar(Co(Code Q(IQ Co CodeIQ
>>(signed C sqrt(I CodeIQ C C C C C;}

◆ポイント

  • #defineで演算子をアルファベット化
  • アルファベット化には文字列CodeIQを用いた
  • #defineされた部分を元に戻しても読みづらい

■出題者コメント
まさにタイトル通り、CodeIQがゲシュタルト崩壊してしまいました

ウッキー竹脇 様

◆タイトル
思いを伝えたい

◆使用言語
Ruby

◆プログラム

#coding = utf-8
def か()
  return gets()
end
def え(ねむい)
  return ねむい.upcase
end
def り(ねむい)
  return ねむい.reverse
end
def た(ねむい)
  return ねむい.split('')
end
def い(ふろはいってない)
  return ふろはいってない.join(' ')
end
print い(た(り(え(か))))

◆ポイント

  • デスマすぎて、とにかく眠くて帰りたい状況を表現してみました

■出題者コメント
リアルデスマ真っ最中に、想いのこもったコードをありがとうございます。
ネタかぶりを心配されていたようですが、全くかぶっていませんでした。
リアルデスマという調味料付きの発想を得ることができたのは、ウッキー竹脇 様のみだったようです。

鍋谷武典 様

トリを務めるのは、出題者でもある 鍋谷武典様です。

◆タイトル
謎言語風

◆使用言語
Ruby

◆プログラム

#coding:utf-8
def static(*);yield if block_given?;:!end
alias void static
alias Main void
class System;def self.in; self;end;def self.eof; STDIN.eof?;end
def self.out;Kernel;end;def self.read; STDIN.getc;end;end
def If(x);yield if ($g=x);end
def Else;yield unless $g;end
def While(x);yield while x.(); end
class  BasicObject;def method_missing(method, *args);
args[0].upcase if args.size==1 && args[0].is_a?( "".class );end;end
alias   lambda

! 謎言語風です。
! 入出力がSystem.inだったりSystem.outだったりする辺りはJava風。
! メインがMainなのはC#風。
! コメントは苦し紛れにFORTRAN風。
! もっとWhileを見慣れた感じにしたかったんだけど、これ以上アイデアがなく。

public static void Main(){
  string s=null;
  While  {! System.in.eof()} {
    If ( s==null ){
      s="";
    }
    Else{
      s=" "+upcase(s);
    }
    s=System.in.read()+s;
  }
  System.out.print(s);
}

◆ポイント

  • 全角空白の悪用
  • Else を実現するためにグロバル変数を濫用
  • method_missing の濫用
  • 実行効率も悪い
  • 全体的に中途半端

■出題者コメント
前半は文法定義部。
後半は自作した新言語部。
あなたは、このトリックを全て読み解けるでしょうか?

さて、この解答については、おそらく鍋谷様のブログ
Codeへの愛とCuriosity
にて、詳細な解説を掲載していただけると思います。
楽しみにお待ちください。

また、鍋谷様は継続してCodeIQの出題を行っております。
ペアレビューのような、非常に細かなフィードバックが人気です。
自信たっぷりに問題を解ける方も、自信がない方も得るものが多いとの噂です。
鍋谷 様 出題ページ

まとめ

逆リファクタリングの解答臭、いかがでしたでしょうか?
他にも面白い解答があったので、ぜひ皆さん、Twitterで公表して情報交換して盛り上がってみてください。

情報の探しさすさを考慮し、下記のフォーマットを推奨します。

ツイート内容 #codeiq #逆リファクタリング

CodeIQ運営事務局より

CodeIQではスキルチェックにぴったりの実力判定問題を出題中!
ぜひ挑戦してみてくださいね!

  • 2296
  • このエントリーをはてなブックマークに追加

■この記事を書いた人

avatar

tbpgr

CodeIQでRubyや様々なカテゴリの問題を出題中。 Twitter:@tbpgr Tbpgr Personal Page: http://tbpgr.github.io/

■関連記事

【コードゴルフ】Ozyさん出題「シンプル・ライフゲーム」問題解説と解答コード公開!... はじめに まずは問題のおさらいです。 【ライフゲーム】 ライフゲームでは、格子状のフィールドの各マス目(セル)に対して、「生」と「死」の2つの状態を初期値として与え、次のルールによって世代変化します。   各セルについて、   「生」の状態の場合 ・周囲の8セルのうち、「生...
世界初!?Opal製Hubotプラグインで解答する「RubyでHubot!?」問題解説 #Ruby ... はじめに 2015年7月3日~2015年7月20日の期間、「RubyでHubot!?」というRuby/Opal/JavaScript複合問題を出題し、5名の方が挑戦してくださいました。 環境構築が必要なことに加え、ネット上に直接的な情報が皆無(おそらくこの問題の解答が世界初のOpal製Hubot...
総勢986名の方が挑戦!ホリエモンからの挑戦状【解説編】 #C++ #Ruby #Python #J... 問題のおさらい まずは、問題についてのおさらいです。今回の問題は以下のような内容でした。 こんにちは。堀江貴文です。 今回は、エンジニアのみなさんにぜひ解いていただきたい問題をCodeIQと一緒に考えてみました。ちょっと考え方に工夫が必要な問題になっていますので、ぜひ挑戦してみてください。 ...
割とあるサンプルコード書く機会 。第2回Rubictionary問題結果発表です! #rubicti... はじめに 出題者のtbpgrです。 当記事では「第2回 Rubictionary」問題の優秀解答発表を行います。 記事の構成は以下のようになります。 出題意図 問題文 サンプルコード紹介 選択問題の出題ミスのお詫び 出題意図 ※出題意図の内容は、第1回 Rubictionary問題結果...
割とあるサンプルコード書く機会。第1回Rubictionary問題結果発表です! #rubictio... はじめに 出題者のtbpgrです。 当記事では「第1回 Rubictionary」問題の優秀解答発表を行います。 記事の構成は以下のようになります。 出題意図 問題文 サンプルコード紹介 出題意図 昨今、 サンプルコードによるコミュケーションやドキュメンテーションの重要度が増しています...
書籍発刊記念問題(プレゼント付)!「わんにゃんキャッスル」解説 #プログラミング #ruby... 問題のおさらい ■ 設問内容 わんにゃん勢力MAPがあります。 彼らは自分たちの権威を誇示するために、できるだけ大きな城を建てようと考えました。 城は、MAPの自勢力上で、正方形の区画になっている部分に建てることができます。 にゃんこ群は3×3の城を建てることができます。わんこ群は、複数の候...

今週のPickUPレポート

新着記事

週間ランキング

CodeIQとは

CodeIQ(コードアイキュー)とは、自分の実力を知りたいITエンジニア向けの、実務スキル評価サービスです。

CodeIQご利用にあたって
関連サイト
codeiq

リクルートグループサイトへ