LoginSignup
17

More than 3 years have passed since last update.

不可視のFizzBuzz (Ruby)

Last updated at Posted at 2015-02-20

某FizzBuzzの記事を読んでたら急にFizzBuzzを書きたくなりました。
Rubyです。

invisible_fizzbuzz.rb
eval [<<"".chomp.tr(" -‍  ", "0-9a-f")].pack("H*")

1
2
Fizz
4
Buzz
Fizz
7

*** しょうりゃく ***

94
Buzz
Fizz
97
98
Fizz
Buzz

ごく普通のFizzBuzzです。
ただし、上のソースをコピペしても絶対に動きません。

コピペじゃ無理なのでGitHubにアップロードしました。
下のリンクから右クリック等で ファイルを直接ダウンロードして 実行してください。
(下のリンクからでも、コピペじゃたぶん無理です)

あと環境によっても動かないかもしれません。
以下の環境で動作確認しました。

Mac OS X 10.10.2
Ruby 2.2.0p0

説明

予想付くかもですが、空白の部分にソースを書き込んであります。
見えてる部分はヒアドキュメント<<""で空行までの文字列(空白)を取り出し、元の文字列に直してevalしてるだけです。

一番上のソースは空白の部分を削除してるので、動くわけがありません。
(どうせ見えないし、コピペしても動かないからいいよね。)

スペースってたくさんあるので、ちょっとした文字列なら隠せます。
さっきのソースのevalをはずしてputsにすると以下のソースが出てきます。

自分なりのFizzBuzz
(1..100).zip(([nil]*2<<:Fizz).cycle,([nil]*4<<:Buzz).cycle){|n,*z|puts z.any?&&z*""||n}

元は以下のようなソースです。
空白が小さいほうがバレにくいので上のように短くしました。

cycleとzipを使ったFizzBuzz
fizz = [nil] * 2 << :Fizz
buzz = [nil] * 4 << :Buzz

(1..100).zip(fizz.cycle, buzz.cycle) do |n, *f|
  puts f.any? ? f.join : n
end

空白に変換するには、見えてる部分のソースと逆のことをやるだけ。

ソースを空白に変換
src = '(1..100).zip(([nil]*2<<:Fizz).cycle,([nil]*4<<:Buzz).cycle){|n,*z|puts z.any?&&z*""||n}'

hex = src.unpack("H*")[0] # 16進数にする
#=>"28312e2e313030292e7a697028285b6e696c5d2a323c3c3a46697a7a292e6379636c652c285b6e696c5d2a343c3c3a42757a7a292e6379636c65297b7c6e2c2a7a7c70757473207a2e616e793f26267a2a22227c7c6e7d"

spaces = hex.tr("0-9a-f", " -‍  ") # 16種のスペースで置き換える
#=> "                                                                                                                                                                              "

空白の文字列はコピペじゃうまく扱えなかったりするので、パイプでファイルに書き出すとかしたほうがいいかも。

スペースは通常の半角・全角に加えて、U+2000U+200Dを使いました。
連番なので扱いやすいです。

16種類のスペース
[*"\u2000".."\u200d"] + [" ", " "]
しらべてみる
spaces = [*"\u2000".."\u200d"] + [" ", " "]

spaces.size #=> 16
spaces.uniq.size #=> 16

# 「/\s/」に引っかかるのは普通の半角スペースだけ。
spaces.grep(/\s/)
#=> [" "]

# 「/[[:space:]]/」にはほとんど引っかかる。引っかからないスペースは3つ。
spaces.reject{|s| /[[:space:]]/ === s }.map{|s| s.ord.to_s(16)}
#=> ["200b", "200c", "200d"]

合わせてちょうど16種類なので16進数を表現できます。
文字列を16進数にすれば完全に隠蔽できます。

trに渡している文字列は以下のようになってます。
見えないので補足しないと何がなんだかわかりませんね。

spaces = "\u2000" + "-" + "\u200d" + " " + " "

スペースの種類はWikipediaにたくさん。

これらの空白は変数名としても使えたので、頭が痛くなるようなソースはいくらでも書けそうです。
全部試したわけではないですが。

 = 1
  
puts 
#=> 1

追記

ってかこれFizzBuzz関係ない。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17