LoginSignup
9
4

More than 5 years have passed since last update.

Rubyの記号プログラミング

Last updated at Posted at 2018-09-20

記述可能な範囲

使えるのが記号だけであるため、主に使えるメソッドに大幅に制限がかかってきます
が、動的言語系列ではこういった制限プログラミングの強い味方が居ます

そう、eval族です

任意の文字列をプログラムとして実行できるevalをなんとかして実行できれば、あらゆるプログラムを記号のみに変換して可動性を大幅に低下させることができます

任意の整数の作り方

まず基本として、任意の整数の作り方です
数値は記号に含まれませんので、何らかの方法で記号の塊から数値を錬成する必要があります

具体的には

num.rb
# 1
'_'=~/$/

# 1
'_'<=>''

このように記述できます
1つ目では、=~ は 正規表現にマッチした位置を返す演算子、/$/は文末にマッチするため、渡した文字列の長さ(この場合は1)が取得できます
2つ目では、<=> は左右を比較してその大小によって-1, 0, 1またはnilのいずれかを返すため、これを利用して1を取得できます
あとはこれを四則演算で組み合わせて任意の数値を作りましょう

任意の文字の作り方

任意の数値が作れるようになったので、次は文字を作りましょう
文字は以下のように、種となる空文字列を破壊的変更していくことで作成します

str.rb

# a
'' << 97

# frozen_string_literal: true の場合は
[]*'' << 97

<<での連結の際に文字コード(数値)から文字への変換が行われるため、これを利用して任意の文字列を作成することができます

eval ……の前に

もう一つ必要なパーツがあります
RubyではProcを ->(){} で表すことができ、また [] や .() や === で呼び出しを行うことができるため、記号だけでProcを使用できます

proc.rb

# 定義
$_ = ->(_, &__){ do_something }

# 呼び出し
$_[hoge]{}
$_.(fuga, &piyo)
$_ === 'hogera'

ちなみに、以下のようなものを用意しておくと、数値の生成が楽になります

num2.rb

$_=->__,_='_'=~/$/{__[-_]? ($_[__[(_-_)...-_]]<<_)+(__[-_]=='_'? _:_-_):_-_}

# 以下と同じ
# $_ = ->(str, num = 1){
#   if str[-1]
#     ($_[str[0...-1]] << 1) + (str[-1] == '_' ? 1 : 0)
#   else
#     0
#   end
# }

# $_['__ _ '] => 26
# 2進数を 1='_', 0=それ以外で表現

eval

さて、いよいよ目標のevalを生成します
そのためには Symbol#to_proc を用います

to_proc.rb

pro = ->(&_){ _.call }
pro.call(&:symbol)
# ->(*args){ args[0].public_send(:symbol, *args[1..-1]) }.call(*args)

このsymbolの部分にevalを渡せばめでたくevalが実行できる、と思ったらevalはプライベートメソッドなのでできません
なので、代わりに instance_eval を用いるかsendからevalを呼び出すか、といった形になります

eval.rb

proc_send = ->(str, &blo){
  blo.call('', 'eval', str)
}
proc_send.call('do something', &:"send") # => do_something がevalで実行される


proc_instance_eval = ->(str, &blo){
  blo.call('', str)
}
proc_instance_eval.call('do something', &:"instance_eval") # => do_something がinstance_evalで実行される

記号のみを使ったHello world

以上を用いてHello worldを出力するプログラムが以下となります

hello.rb

$_=->__,_='_'=~/$/{__[-_]? ($_[__[(_-_)...-_]]<<_)+(__[-_]=='_'? _:_-_):_-_}
$__=->_,&__{__['',[]*''<<$_['__  _ _']<<$_['___ __ ']<<$_['__    _']<<$_['__ __  '],_]}

@_ = []*''<<$_['___    ']<<$_['___ _ _']<<$_['___ _  ']<<$_['___  __']<<$_['_     ']<<$_['_  ___']<<
$_['_  _   ']<<$_['__  _ _']<<$_['__ __  ']<<$_['__ __  ']<<$_['__ ____']<<$_['_ __  ']<<$_['_     ']<<
$_['___ ___']<<$_['__ ____']<<$_['___  _ ']<<$_['__ __  ']<<$_['__  _  ']<<$_['_    _']<<$_['_  ___']

$__[@_,&:"#{[]*''<<$_['___  __']<<$_['__  _ _']<<$_['__ ___ ']<<$_['__  _  ']}"]

なんだかシンタックスハイライトがおかしなことになっていますが動くはずです、動くんじゃないかな、多分動くと思う ……

文字→数値の変換は String#ord を用いれば可能であり、任意のプログラムを変換することが可能ですので、そちらは宿題ということで興味があればどうぞ

任意のプログラムを変換するプログラムを変換したものを最後に載せようと思っていましたが、すごく長くなったのでやめたのは秘密です

長い間ありがとうございました、既出でしたらすみません

9
4
0

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
9
4