Factor で Welcome to AtCoder を解いてみた

[Qiita Engineer Festa 2024（キータ・エンジニア・フェスタ 2024） - Qiita](https://qiita.com/official-campaigns/engineer-festa/2024)
において、約1ヶ月で38記事という大量の記事の投稿を要求されることがわかった。
そこで、あまりコストをかけずに記事数を稼ぐ方法を考えた結果、「Welcome to AtCoder を様々な言語で解く」ことを思いついた。
単に解くだけでなく、使用する言語仕様の解説を入れれば、記事として一応成立するだろう。

# Welcome to AtCoder

[PracticeA - Welcome to AtCoder](https://atcoder.jp/contests/abs/tasks/practice_1)

Welcome to AtCoder では、以下の形式で整数 $a$, $b$, $c$ および文字列 $s$ が入力として与えられる。

```text
a
b c
s
```

この入力をもとに、与えられた整数の和 $sum = a + b + c$ および文字列 $s$ を、以下の形式で出力することが求められる。

```text
sum s
```

# 今回用いた Factor の機能

[Factor cookbook - Factor Documentation](https://docs.factorcode.org/content/article-cookbook.html)

## プログラムの要素

Factor のプログラムには、以下の要素がある。

### 値

プログラム中に値を書くと、その値をスタックに積むという意味になる。
今回用いる値には、数値、`t` (真)、`f` (偽) がある。

### word

word とは、命令のようなものである。
スタックに積まれている要素を必要に応じて参照して処理を行い、追加・削除・変更を行う。

### USING

```factor
USING: 使用するライブラリのリスト ;
```

使用するライブラリ (vocabulary) をまとめて読み込み、それらのライブラリに含まれる word を使用できるようにする。

### コメント

各行の `!` 以降の部分はコメントである。

### quotation

quotation とは、命令列を `[` と `]` で囲んだものである。
命令のブロックを作成し、フロー制御を行う word の入力とする用途などに用いる。

### 配列

要素の列を `{` と `}` で囲むことで、配列を作成できる。

## 今回用いた word

以下の「意味」において、「1番目」はスタックトップを、「2番目」はスタックトップの次の要素を、「3番目」はスタックトップの次の次の要素を表す。
プッシュやポップなどのスタック操作を行う場合でも、これらは操作開始時の要素を表す。

### 演算・論理

|vocabulary|word|意味|
|:-:|:-:|---|
|[math](https://docs.factorcode.org/content/vocab-math.html)|[`*`](https://docs.factorcode.org/content/word-__star__%2Cmath.html)|1番目と2番目をポップし、これらの積をプッシュする|
|[math](https://docs.factorcode.org/content/vocab-math.html)|[`+`](https://docs.factorcode.org/content/word-%2B%2Cmath.html)|1番目と2番目をポップし、これらの和をプッシュする|
|[math](https://docs.factorcode.org/content/vocab-math.html)|[`-`](https://docs.factorcode.org/content/word--%2Cmath.html)|1番目と2番目をポップし、<br>2番目から1番目を引いた差をプッシュする|
|[math](https://docs.factorcode.org/content/vocab-math.html)|[`/mod`](https://docs.factorcode.org/content/word-__slash__mod%2Cmath.html)|1番目と2番目をポップし、2番目を1番目で割る<br>商をプッシュし、続いて余りをプッシュする
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`=`](https://docs.factorcode.org/content/word-%3D%2Ckernel.html)|1番目と2番目をポップし、<br>これらが等しいかを表す値をプッシュする|
|[math](https://docs.factorcode.org/content/vocab-math.html)|[`<`](https://docs.factorcode.org/content/word-__lt__%2Cmath.html)|1番目と2番目をポップし、<br>2番目が1番目未満かを表す値をプッシュする|
|[math](https://docs.factorcode.org/content/vocab-math.html)|[`>=`](https://docs.factorcode.org/content/word-__gt__%3D%2Cmath.html)|1番目と2番目をポップし、<br>2番目が1番目以上かを表す値をプッシュする|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`and`](https://docs.factorcode.org/content/word-and%2Ckernel.html)|1番目と2番目をポップし、<br>これらが両方真なら真 (1番目の値)を、<br>そうでなければ偽 (`f`) をプッシュする|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`not`](https://docs.factorcode.org/content/word-not%2Ckernel.html)|1番目をポップし、これが真なら偽 (`f`) を、<br>そうでなければ真 (`t`) をプッシュする|

### スタック操作

|vocabulary|word|意味|
|:-:|:-:|---|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`drop`](https://docs.factorcode.org/content/word-drop%2Ckernel.html)|1番目をポップし、捨てる|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`dup`](https://docs.factorcode.org/content/word-dup%2Ckernel.html)|1番目をプッシュする (コピーする)|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`over`](https://docs.factorcode.org/content/word-over%2Ckernel.html)|2番目をプッシュする (コピーする)|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`swap`](https://docs.factorcode.org/content/word-swap%2Ckernel.html)|1番目と2番目の値を入れ替える|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`swapd`](https://docs.factorcode.org/content/word-swapd%2Ckernel.html)|2番目と3番目の値を入れ替える|

### フロー制御

[Control flow cookbook - Factor Documentation](https://docs.factorcode.org/content/article-cookbook-combinators.html)

|vocabulary|word|意味|
|:-:|:-:|---|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`if`](https://docs.factorcode.org/content/word-if%2Ckernel.html)|1番目・2番目・3番目をポップし、<br>3番目が `f` (偽) なら1番目を、<br>そうでなければ2番目を実行する|
|[kernel](https://docs.factorcode.org/content/vocab-kernel.html)|[`loop`](https://docs.factorcode.org/content/word-loop%2Ckernel.html)|1番目をポップし、実行する<br>実行後スタックトップをポップし、<br>それが `f` (偽) でなければ実行を (何回でも) 繰り返す|
|[math](https://docs.factorcode.org/content/vocab-math.html)|[`times`](https://docs.factorcode.org/content/word-times%2Cmath.html)|1番目と2番目をポップし、<br>1番目を、2番目で指定する回数繰り返し実行する|

### 配列操作

|vocabulary|word|意味|
|:-:|:-:|---|
|[sequences](https://docs.factorcode.org/content/vocab-sequences.html)|[`prefix`](https://docs.factorcode.org/content/word-prefix%2Csequences.html)|1番目と2番目をポップし、2番目の配列の先頭に<br>1番目の要素を追加した配列をプッシュする|

### 標準入出力

|vocabulary|word|意味|
|:-:|:-:|---|
|[io](https://docs.factorcode.org/content/vocab-io.html)|[`read1`](https://docs.factorcode.org/content/word-read1%2Cio.html)|標準入力から1バイト読み込み、数値としてプッシュする<br>EOF の場合は `f` をプッシュする|
|[io](https://docs.factorcode.org/content/vocab-io.html)|[`write`](https://docs.factorcode.org/content/word-write%2Cio.html)|1番目をポップし、その配列の要素を順に<br>バイトとして標準出力に出力する|
|[io](https://docs.factorcode.org/content/vocab-io.html)|[`write1`](https://docs.factorcode.org/content/word-write1%2Cio.html)|1番目をポップし、その値を標準出力に (1バイト) 出力する|

# 提出コード

```factor
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
```

[提出 #55331433 - AtCoder Beginners Selection](https://atcoder.jp/contests/abs/submissions/55331433)