Qiita Engineer Festa 2024(キータ・エンジニア・フェスタ 2024) - Qiita
において、約1ヶ月で38記事という大量の記事の投稿を要求されることがわかった。
そこで、あまりコストをかけずに記事数を稼ぐ方法を考えた結果、「Welcome to AtCoder を様々な言語で解く」ことを思いついた。
単に解くだけでなく、使用する言語仕様の解説を入れれば、記事として一応成立するだろう。
Welcome to AtCoder
PracticeA - Welcome to AtCoder
Welcome to AtCoder では、以下の形式で整数 $a$, $b$, $c$ および文字列 $s$ が入力として与えられる。
a
b c
s
この入力をもとに、与えられた整数の和 $sum = a + b + c$ および文字列 $s$ を、以下の形式で出力することが求められる。
sum s
今回用いた Factor の機能
Factor cookbook - Factor Documentation
プログラムの要素
Factor のプログラムには、以下の要素がある。
値
プログラム中に値を書くと、その値をスタックに積むという意味になる。
今回用いる値には、数値、t (真)、f (偽) がある。
word
word とは、命令のようなものである。
スタックに積まれている要素を必要に応じて参照して処理を行い、追加・削除・変更を行う。
USING
USING: 使用するライブラリのリスト ;
使用するライブラリ (vocabulary) をまとめて読み込み、それらのライブラリに含まれる word を使用できるようにする。
コメント
各行の ! 以降の部分はコメントである。
quotation
quotation とは、命令列を [ と ] で囲んだものである。
命令のブロックを作成し、フロー制御を行う word の入力とする用途などに用いる。
配列
要素の列を { と } で囲むことで、配列を作成できる。
今回用いた word
以下の「意味」において、「1番目」はスタックトップを、「2番目」はスタックトップの次の要素を、「3番目」はスタックトップの次の次の要素を表す。
プッシュやポップなどのスタック操作を行う場合でも、これらは操作開始時の要素を表す。
演算・論理
| vocabulary | word | 意味 |
|---|---|---|
| math | * |
1番目と2番目をポップし、これらの積をプッシュする |
| math | + |
1番目と2番目をポップし、これらの和をプッシュする |
| math | - |
1番目と2番目をポップし、 2番目から1番目を引いた差をプッシュする |
| math | /mod |
1番目と2番目をポップし、2番目を1番目で割る 商をプッシュし、続いて余りをプッシュする |
| kernel | = |
1番目と2番目をポップし、 これらが等しいかを表す値をプッシュする |
| math | < |
1番目と2番目をポップし、 2番目が1番目未満かを表す値をプッシュする |
| math | >= |
1番目と2番目をポップし、 2番目が1番目以上かを表す値をプッシュする |
| kernel | and |
1番目と2番目をポップし、 これらが両方真なら真 (1番目の値)を、 そうでなければ偽 ( f) をプッシュする |
| kernel | not |
1番目をポップし、これが真なら偽 (f) を、そうでなければ真 ( t) をプッシュする |
スタック操作
| vocabulary | word | 意味 |
|---|---|---|
| kernel | drop |
1番目をポップし、捨てる |
| kernel | dup |
1番目をプッシュする (コピーする) |
| kernel | over |
2番目をプッシュする (コピーする) |
| kernel | swap |
1番目と2番目の値を入れ替える |
| kernel | swapd |
2番目と3番目の値を入れ替える |
フロー制御
Control flow cookbook - Factor Documentation
| vocabulary | word | 意味 |
|---|---|---|
| kernel | if |
1番目・2番目・3番目をポップし、 3番目が f (偽) なら1番目を、そうでなければ2番目を実行する |
| kernel | loop |
1番目をポップし、実行する 実行後スタックトップをポップし、 それが f (偽) でなければ実行を (何回でも) 繰り返す |
| math | times |
1番目と2番目をポップし、 1番目を、2番目で指定する回数繰り返し実行する |
配列操作
| vocabulary | word | 意味 |
|---|---|---|
| sequences | prefix |
1番目と2番目をポップし、2番目の配列の先頭に 1番目の要素を追加した配列をプッシュする |
標準入出力
| vocabulary | word | 意味 |
|---|---|---|
| io | read1 |
標準入力から1バイト読み込み、数値としてプッシュする EOF の場合は f をプッシュする |
| io | write |
1番目をポップし、その配列の要素を順に バイトとして標準出力に出力する |
| io | write1 |
1番目をポップし、その値を標準出力に (1バイト) 出力する |
提出コード
USING: kernel math sequences io ;
! ----------- 3個の数値の和を求める -----------
0 ! 和となる数値をスタックに積む
3 [ ! times により3回繰り返す
0 ! 1個の数値を読み込んだ結果となる数値をスタックに積む
[ ! 1個の数値の読み込みを行う (loop)
read1 ! 1文字読み込む
dup 48 >= ! 読み込んだ文字が '0' 以上かを調べる
over 58 < ! 読み込んだ文字が '9' 以下かを調べる ('0' 以上かがあるので、over を用いる)
and ! 2個の判定を組み合わせ、読み込んだ文字が数字かを調べる
[ ! 読み込んだ文字が数字である (t) 場合、以下の処理を行う
48 - ! 読み込んだ文字(コード)から '0' を引き、数値に変換する
swap 10 * + ! これまでの変換結果の10倍に、変換した数値を足す (変換結果を更新する)
t ! 変換のループを続行する
]
[ ! 読み込んだ文字が数字でない (f) 場合、以下の処理を行う
drop ! 読み込んだ文字を捨てる
f ! 変換のループを終了する
]
if ! 読み込んだ文字が数字かによって、上記の処理を行う
] loop
+ ! 読み込んだ数値を和に加算する
] times
! ----------- 求めた和を十進数で表現し、出力する -----------
{ } swap ! 和(数値)の後ろに、十進数表現を格納する配列を置く
[ ! 和(数値)を十進数に変換する (loop)
10 /mod ! 和(数値)を10で割り、商と余りを求める
48 + ! 求めた余りに '0' を足し、数字に変換する
! この時点でのスタック:配列 商 数字
swapd ! 数字を配列に追加するため、配列を移動する
! この時点でのスタック:商 配列 数字
prefix ! 数字を配列の先頭に追加する
! この時点でのスタック:商 配列
swap ! 配列の位置を戻す
! この時点でのスタック:配列 商
dup 0 = not ! 残っている商が0でないかを判定する (0でない間ループを続ける)
] loop
drop ! 商 (0) を捨てる
write ! 変換結果の配列を出力する
! ----------- 値と文字列の間の空白を出力する -----------
32 write1 ! 数値と文字列の間の空白を出力する
! ----------- 文字列 s を出力する -----------
[ ! 文字列の出力を行う (loop)
read1 ! 1文字読み込む
dup write1 ! 読み込んだ文字を確認できるようにコピーし、出力する
10 = not ! 読み込んだ文字が '\n' かを判定する ('\n' でない間ループを続ける)
] loop