Scheme で 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
```

# 今回使用する Scheme の言語仕様

## 関数呼び出し

[4.1.3 Procedure calls](https://people.csail.mit.edu/jaffer/r5rs_6.html#SEC28)

カッコの中に空白区切りで呼び出す関数と引数を書く。
最初の項目が呼び出す関数、2番目以降の項目がその関数に渡す引数である。

```
(関数 引数1 引数2 ...)
```

関数を引数なしで呼び出すこともできる。

```
(関数)
```

## 文字

[6.3.4 Characters](https://people.csail.mit.edu/jaffer/r5rs_8.html#SEC61)

`#\文字` の形式で、文字定数を表せる。
空白文字は `#\space` と表せる。

## 値の定義

[5.2 Definitions](https://people.csail.mit.edu/jaffer/r5rs_7.html#SEC44)

`define` を用いると、識別子に値を割り当てることができる。

```
(define 識別子 割り当てる値)
```

## 値の定義 (ローカル)

[4.2.2 Binding constructs](https://people.csail.mit.edu/jaffer/r5rs_6.html#SEC34)

`let` を用いると、その内側だけで使う識別子に値を割り当てることができる。

```
(let (割り当てリスト) 計算内容)
```

「割り当てリスト」は、`(識別子 割り当てる値)` の形式の要素のリストである。

## 関数定義

[4.1.4 Procedures](https://people.csail.mit.edu/jaffer/r5rs_6.html#SEC29)

`lambda` を用いると、関数を定義できる。

```
(lambda (引数リスト) 計算内容)
```

引数リストは、引数を受け取り、「計算内容」で用いることができる識別子のリストである。

## 遂次実行

[4.2.3 Sequencing](https://people.csail.mit.edu/jaffer/r5rs_6.html#SEC35)

`begin` を用いると、複数の式を順番に評価できる。

```
(begin 式1 式2 ...)
```

## 選択

[4.1.5 Conditionals](https://people.csail.mit.edu/jaffer/r5rs_6.html#SEC30)

`if` を用いると、条件の真偽によって評価する式を切り替えることができる。

```
(if 条件式 条件式が偽でないとき評価する式 条件式が偽のとき評価する式)
```

## 加算

[6.2.5 Numerical operations](https://people.csail.mit.edu/jaffer/r5rs_8.html#SEC55)

`+` を用いると、引数として与えた数値の和を求めることができる。

```
(+ 数値1 数値2 ...)
```

## 入出力

[6.6 Input and output](https://people.csail.mit.edu/jaffer/r5rs_8.html#SEC66)

以下の関数により、入力の読み取りや出力を行うことができる。

|関数|意味|
|---|---|
|`(read)`|オブジェクト (数値など) を1個読み取る|
|`(read-char)`|1文字読み取る|
|`(eof-object? 値)`|値がEOFオブジェクトかを判定する|
|`(write オブジェクト)`|オブジェクトを1個出力する|
|`(write-char 文字)`|1文字出力する|

`(read-char)` は通常読み取った文字を返すが、入力の終端に達していた場合はEOFオブジェクトを返す。

# 提出コード

```scheme
(write (+ (read) (read) (read)))
(write-char #\space)
(read-char)
(define f (lambda () (let ((c (read-char)))
  (if (eof-object? c) () (begin
    (write-char c)
    (f)
  ))
)))
(f)
```

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

# 提出コードの解説

```scheme
(write (+ (read) (read) (read)))
```

整数 $a$, $b$, $c$ をそれぞれ読み込み、それらの和を出力する。

```scheme
(write-char #\space)
```

出力において数値と文字列を区切る空白を出力する。

```scheme
(read-char)
```

入力の数値の後にある改行文字を読み飛ばす。

```scheme
(define f (lambda () (let ((c (read-char)))
  (if (eof-object? c) () (begin
    (write-char c)
    (f)
  ))
)))
```

以下の処理を行う関数 `f` を定義する。
まず、1文字読み込んで識別子 `c` に割り当てる。
続いて、`c` がEOFオブジェクトかをチェックする。
`c` がEOFオブジェクトである場合は、処理を終了する。
そうでない場合は、読み込んだ文字 `c` を出力し、この処理を繰り返す。

すなわち、これは「残りの入力の最後まで読み込んで出力する」処理の定義である。

```scheme
(f)
```

定義した「残りの入力の最後まで読み込んで出力する」処理を実行する。
これにより、入力の文字列が出力される。
これには、最後の改行も含まれる。