8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 9

elixirでQuineを実装する

Last updated at Posted at 2024-12-08

quineとは

quineとは「自分自身の文字列を出力するプログラム」のことです。
wikiにはいくつかの言語で実装されたquineのサンプルがあります。

この記事ではelixirでQuineを実装しようと思います。

quineの実装

あなたの知らない超絶技巧プログラミングの世界 という本にQuineの基本的な書き方が載っていました。

Quineの基本的な書き方を引用します。

(1) 元のプログラム文字列の種となる文字列sを用意する。
(2) s自身を使ってsの中の一部を適切に書き換え、元のプログラム文字列を再構築する
(3) 出力する

(2)がややこしいので、(1)と(3)のみを実装したものから始めます。

本ではrubyで説明されていました。

s = "..."; print s

elixirもここから実装を始めてみます。

s="...";s|>IO.puts

ここから(2)の部分を追加していきます。
まずは「…」に自分自身を埋め込みます。

s="s=\"...\";s|>IO.puts";s|>IO.puts

"(ダブルクォテーション)はエスケープしています。

iex> s="s=\"...\";s|>IO.puts";s|>IO.puts
s="...";s|>IO.puts
:ok

このままでは、変数sしか出力できないので、「…」をsで置換します。
rubyではsubを使用していました。
elixirではString.replaceを使用します。

s="s=\"...\";s|>IO.puts";String.replace(s,"...",s,global: false)|>IO.puts
実行結果
iex> s="s=\"...\";s|>IO.puts";String.replace(s,"...",s,global: false)|>IO.puts
s="s="...";s|>IO.puts";s|>IO.puts
:ok

String.replaceを追加したので、変数sも変更します。

s="s=\"...\";String.replace(s,\"...\",s,global: false)|>IO.puts";String.replace(s,"...",s,global: false)|>IO.puts
実行結果
iex> s="s=\"...\";String.replace(s,\"...\",s,global: false)|>IO.puts";String.replace(s,"...",s,global: false)|>IO.puts
s="s="...";String.replace(s,"...",s,global: false)|>IO.puts";String.replace(s,"...",s,global: false)|>IO.puts
:ok

ほぼ一致しましたが、"s=\"...\"の部分が"s="..."になっています。

rubyでも同様の問題があり、置換前にdumpを使って解消していました。
elixirではinspectを使用してみます。(変数sも調整します)

s="s=\"...\";String.replace(s,\"...\",inspect(s),global: false)|>IO.puts";String.replace(s,"...",inspect(s),global: false)|>IO.puts
実行結果
iex> s="s=\"...\";String.replace(s,\"...\",inspect(s),global: false)|>IO.puts";String.replace(s,"...",inspect(s),global: false)|>IO.puts
s=""s=\"...\";String.replace(s,\"...\",inspect(s),global: false)|>IO.puts"";String.replace(s,"...",inspect(s),global: false)|>IO.puts
:ok

rubyではここで解決していました。
がelixirだとs="s=\"の部分がs=""s=\"になっています。

String.replace(\"\", \")を追加すればいいのですが、このまま追加すると変数sの調整ができなくなります。
そこでASCIIコードを利用して、String.replace(<<34, 34>>, <<34>>)を追加します。

s="s=\"...\";String.replace(s,\"...\",inspect(s),global: false)|>String.replace(<<34, 34>>, <<34>>)|>IO.puts";String.replace(s,"...",inspect(s),global: false)|>String.replace(<<34, 34>>, <<34>>)|>IO.puts
実行結果
iex> s="s=\"...\";String.replace(s,\"...\",inspect(s),global: false)|>String.replace(<<34, 34>>, <<34>>)|>IO.puts";String.replace(s,"...",inspect(s),global: false)|>String.replace(<<34, 34>>, <<34>>)|>IO.puts
s="s=\"...\";String.replace(s,\"...\",inspect(s),global: false)|>String.replace(<<34, 34>>, <<34>>)|>IO.puts";String.replace(s,"...",inspect(s),global: false)|>String.replace(<<34, 34>>, <<34>>)|>IO.puts
:ok

rubyより1つ処理が増えましたが、Quineを実装できました。

実行結果の確認

Quineが実装できたどうかを確認します。

quine.exsを作成します。

quine.exs
s="s=\"...\";String.replace(s,\"...\",inspect(s),global: false)|>String.replace(<<34, 34>>, <<34>>)|>IO.puts";String.replace(s,"...",inspect(s),global: false)|>String.replace(<<34, 34>>, <<34>>)|>IO.puts

出力した結果をファイルに保存し比較します。

elixir quine.exs > quine.txt
diff quine.exs quine.txt

また出力したquine.txtもelixirとして実行できます。

cp quine.txt quine2.exs
elixir quine2.exs

その他のQuine

elixirでQuineの実装例を調べてみました。

いくつか実装例がありますが、これがelixirらしくて好きでした。

"fn x->IO.puts~s(\#{inspect x}|>\#{x}) end.()"|>fn x->IO.puts~s(#{inspect x}|>#{x}) end.()
8
0
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
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?