競プロの問題をnibblesで解く③

# はじめに
nibblesで問題を解くの会 第三回です。
[一回目](https://qiita.com/i_taku_/items/e85f9f4ea482e35dfadd)、[二回目](https://qiita.com/i_taku_/items/623506d5e6ac2b41c1b9)もぜひご覧ください。

今回はmapを扱います。
filterもやろうと思いましたが、思ったより分量が多くなってしまったので、こちらはまた次回で。

この辺りから可読性が終わってきます。
最初はきっと、コードを読んでも何もわからないと思いますが、頑張りましょう。
公式の[Quick Ref](http://www.golfscript.com/nibbles/quickref.html)を見ながらだと理解が進みやすいと思います。

ちなみに、nibblesでコメント付けられるの最近まで知らず...今回から活用していきます。
「 # 」以降がコメントになるみたいです。pythonと一緒。

今回はmapの概要を説明した後に問題を解いてみたいと思います。
最終的に、[ドキュメント](https://www.golfscript.com/nibbles/index.html)に載っている下記FizzBuzzのコードを解読を目指します！
```nibbles:FizzBuzz 28byte
$?,:^-~%$3"Fizz"^-~%$5"Buzz"
```

今回もフォーマットはkotatsugameさんの[こちらの記事](https://qiita.com/kotatsugame/items/184bfd63d9b21f214475)から、丸パクリさせていただきました。（いつもありがとうございます。）

> 入力形式

解法

```:Byte数
コード
``` 
コメント

# mapでできること
連想配列ではないです。
pythonのmapと基本同じ、内包表記も近いです。
そのため、一部解説の中でpythonの疑似コードを使用しています。（pythonわからない方ごめんなさい）

nibblesにはforがないので、多くの問題でmapを使用することになります。
mapの演算を[Quick Ref](https://www.golfscript.com/nibbles/quickref.html)で確認します。
|演算子|引数1|引数2|
|:---:|:---:|:---:|
|.|list|fn|

（「 . 」が演算子なのちょっと不思議な感じしませんか？）
map 、第二引数にfn（関数）を取ります。
文字で説明するのも難しいので、実際にコードを見ていきます。

## 例１）長さ5、要素が全て10の配列を作成してください
pythonの内包表記と上の表のlist, fnを対応させてみました。
```python:python
map(lambda _: 10, range(5))  # map(fn, list)
[10 for _ in range(5)]  # [fn for _ in list]
```
nibblesではこちら

```nibbles:nibbles
.    # map
,5  # list （ ,x で 1 〜 x のリストを作成）
10   # fn
# -> [10, 10, 10, 10, 10]
```
まだ簡単です。

## 例２）10〜15のリストを作成してください
先ほどと同様に、まずはpythonを見てみます。
rangeでそのまま実現できますが、今回はこちらで。
```python:python
map(lambda x: x + 10, range(6))  # map(fn, list)
[x + 10 for x in range(6)]  # [fn for _ in list]
```
nibblesでこの x をどうやって扱うかがポイントです。
nibblesでは、下記の通り入力を受け取れました。
| Arg | 名前 |型|
|:---:|:---:|:---:|
| $   | fstInt    | Integer | 
| @   | fstLine   | String |
| _   | ints      | [Integer] |
| ;$  | sndInt    | Integer |
| ;@  | allLines  | [String] | 
| ;_  | allInput  | String |
| ;;$ | intMatrix | [[Integer]] |
| ;;@ | sndLine   | String | 

これに対してmapをしてみます。
```nibbles:map
.    # map
`,6  # 0 ~ 6 のリストを生成  （ , を `, にすると 0 ~ x - 1のリストが作れます）
```
実はこのmapで先ほどの表がアップデートされます。
|Arg|New|map前|
|:---:|:---:|:---:|
| $   | x(listの中身) | fstInt    |
| @   | fstInt       | fstLine   |
| _   | fstLine      | ints      |
| ;$  | ints         | sndInt    |
| ;@  | sndInt       | allLines  |
| ;_  | allLines     | allInput  |
| ;;$ | allInput     | intMatrix |
| ;;@ | intMatrix    | sndLine   |
| ;;_ | sndLine      | -         |

一個ずつ押し出されるイメージです。
そんなわけで、x + 10 は + $ 10 をすれば良いです。
```nibbles:map
.     # map
`,6   # 0 ~ 10 のリストを生成
+$10  # 足し算
# -> [10, 11, 12, 13, 14, 15]
```

同じ記号でも意味がどんどん変わってくるのでコードを読むのが難しくなってくるわけです。
mapの中でmapをするとさらに1個分押し出され、押し出され、、逆にmapのスコープを外れるとなんと1個分戻ります。

## 変数迷子にならないために
変数迷子にならないために、 ct を使ってデバッグすることが重要になってきます。
ct をするとエラー出力で先ほどの変数の表を作ってくれます。
色々見てみましょう。
### 例１） ctするだけ
入力の受け取り方がそのまま見れます。
```nibbles:ct
ct
```
```:出力
nibbles: 
Context:
LetArg inputs
  $ fstInt :: Integer 
  @ fstLine :: String 
  _ ints :: [Integer] 
  ;$ sndInt :: Integer 
  ;@ allLines :: [String] 
  ;_ allInput :: String 
  ;;$ intMatrix :: [[Integer]] 
  ;;@ sndLine :: String 

at line: 1, char: 1
v
ct
^
```
### 例２） stringをmap
```nibbles:ct
.   # map
"abcde" # stringもOK
ct  # ctを設置した時点での変数の状況を出力
```
```:出力
nibbles: 
Context:
LambdaArg .
  $  :: Char (unused so far, prioritized for implicit args)

LetArg inputs
  @ fstInt :: Integer 
  _ fstLine :: String 
  ;$ ints :: [Integer] 
  ;@ sndInt :: Integer 
  ;_ allLines :: [String] 
  ;;$ allInput :: String 
  ;;@ intMatrix :: [[Integer]] 
  ;;_ sndLine :: String 


at line: 3, char: 1
v
ct
^
```
### 例３） たくさんmap
```nibbles:map map map map...
.,10 .,10 .,10 ."abcde" .,10  # たくさんmapしてみる
ct  # この時点での状況を出力
.,10 .,10  # 出力には影響しない部分
```
```:出力
nibbles: 
Context:
LambdaArg .
  $  :: Integer (unused so far, prioritized for implicit args)

LambdaArg .
  @  :: Char (unused so far, prioritized for implicit args)

LambdaArg .
  _  :: Integer (unused so far, prioritized for implicit args)

LambdaArg .
  ;$  :: Integer (unused so far, prioritized for implicit args)

LambdaArg .
  ;@  :: Integer (unused so far, prioritized for implicit args)

LetArg inputs
  ;_ fstInt :: Integer 
  ;;$ fstLine :: String 
  ;;@ ints :: [Integer] 
  ;;_ sndInt :: Integer 
  ;;;$ allLines :: [String] 
  ;;;@ allInput :: String 
  ;;;_ intMatrix :: [[Integer]] 
  ;;;;$ sndLine :: String 


at line: 2, char: 1
v
ct
^
```

# 問題
## [Chokudai SpeedRun 002 A - 長方形 α](https://atcoder.jp/contests/chokudai_S002/tasks/chokudai_S002_a)

> $ N $
> $ A_1 \quad B_1 $
> $ A_2 \quad B_2 $
> $ \vdots $
> $ A_N \quad B_N $

各$ A_i, B_i (i = 1, 2 \cdots N)$に対して、 $ A_i \times B_i $を出力します。

```nibbles:7byte
>1;;$`*
```
実際に問題を解いていきます。
ただ、散々mapの話をしたのにいきなり演算子 . が存在しませんね。一旦気にしないでください。

順番に見ていきます。
まず入力を ;;\\$ で受け取ります。
;;\$ は数値行をまとめて二次元配列としてアクセスできます。
pythonで次のことをしているイメージ。
```python:;;$
[list(map(int, input().split())) for _ in range(XX)]
```

一行目の$N$は不要ですので、 drop > を使用して読み飛ばします。
```nibbles:drop
# > x で 配列を先頭から x　個分進める
# 例　（pでデバッグできます）
p >3 ,5   # [4,5]
p >6 ,5   # []  要素数より多くてもOK

# 二次元配列に対するdrop
p .,3 ,3  # mapで二次元配列作成 [[1,2,3],[1,2,3],[1,2,3]]
p >1 .,3 ,3  # [[1,2,3],[1,2,3]]
```

\>1;;\\$で$ N $を読み飛ばして、 map することで、各$ A_i \quad B_i $に \$ でアクセスできるようになります。

```nibbles:map
.>1;;$ ct
# nibbles: 
# Context:
# LambdaArg .
#   $  :: [Integer] (unused so far, prioritized for implicit args)
# 
# LetArg inputs
#   @ fstInt :: Integer 
#   _ fstLine :: String 
#   ;$ ints :: [Integer] 
#   ;@ sndInt :: Integer 
#   ;_ allLines :: [String] 
#   ;;$ allInput :: String 
#   ;;@ intMatrix :: [[Integer]] 
#   ;;_ sndLine :: String 
# 
# 
# at line: 1, char: 8
#        v
# .>1;;$ ct
# ^

# 入力例1
.>1;;$ p$
# [3,4]
# [1000000000,1]
# [111111111,111111111]
```
ctで型を確認すると、しっかりと$がIntegerの配列になっていますね。
Integer配列に対して product `* をすると総積が求められるので、

```nibbles:solve
.>1;;$ `*$

.>1;;$`*$  # 不要なスペース削除
.>1;;$`*   # $ 省略
```

さらに、[こちら](https://www.golfscript.com/nibbles/tutorial_minutiae.html)のImplicit Opsの欄を見ると、下記の記述があります。
> If you don't use the accumulator (@) then it will instead assume you wanted to do a map.

簡単に言うと、引数が \$ だけのとき、map演算が省略できる場合があるよってことです。
今回はしっかり省略できるので、最終形は下記となります。
```:7byte
>1;;$`*
```

## [ABC307 A - Weekly Records](https://atcoder.jp/contests/abc307/tasks/abc307_a)
> $ N $
> $ A_1 \quad A_2 \quad \ldots \quad A_{7N} $

$ A $を7要素ずつ区切って、それぞれ $ sum $ を出力します。

```nibbles:9byte
`/7=2;;$+
```
chunk of を使用して、2行目を7個ずつに分割します。
```nibbles:chunk of
p `/7 =2;;$  # `/x で配列を x個ずつの要素に分解
# 入力例1
# [[1000,2000,3000,4000,5000,6000,7000],
#  [2000,3000,4000,5000,6000,7000,8000]]
```

あとは map を使ってそれぞれの要素を sum してあげればOKです。

```nibbles:solve
.`/7=2;;$+$  # map して sum
`/7=2;;$+    # 先頭の map と最後の $ を省略して完成
```

## FizzBuzz
※入力なし

1 ~ 100 の各要素に対して、3の倍数のとき Fizz, 5の倍数のとき Buzz, 3の倍数かつ5の倍数のとき FizzBuzz, それ以外のときは数字のまま出力します。
```nibbles:28byte
$?,:^-~%$3"Fizz"^-~%$5"Buzz"
```

最後にFizzBuzzを扱っていきます。
FizzBuzzは非常に有名な問題ですね。詳細は[こちら](https://ja.wikipedia.org/wiki/Fizz_Buzz)から。

いままでの問題に対して、かなり難しめですので、気になった方だけ挑戦してみてください！

まずは、「3の倍数に対して、Fizzを出力する」処理だけ抜き出してみます。
これだけでもややこしいので、[Quick Ref](https://www.golfscript.com/nibbles/quickref.html)を横に並べてご覧ください！
```nibbles:3の倍数に対して、Fizzを出力
^-~%$3"Fizz"

# さらに処理を分解
^  # replicate 指定回数文字列を繰り返す
   # 例) ^3"ab" → "ababab"
   # 回数が0以下のときは空文字列

-~ %$3  # - 1 %$3 と同義。
        # 3の倍数のときのみ、 ^ の回数を1とする処理

"Fizz"  # 繰り返し対象の文字列

# テスト
p ^-~%$3"Fizz"

# 入力 出力
# 1   ""
# 2   ""
# 3   "Fizz"
```
<details><summary>-~ %$3 の補足</summary>

普通の引き算をしているだけなんですが、 ~ を使用しています。

~ を使用すると、[Quick Ref](https://www.golfscript.com/nibbles/quickref.html)のAutos列の値が使用されます。
\- (subtract) のAutosを確認すると 「$1 \quad 1$」 とあります。
第一引数、第二引数それぞれで ~ を使うと 1 として処理されますよ、という意味です。

「 -1\\$ 」のように書くと、「-1」というかたまりで扱われてしまうので、\- と1の間にスペースが必要なんですね。
~を使用することで「 -~$ 」と短縮できるわけです。

</details>

Buzzでも「 ^-~%$5"Buzz" 」と、同様の処理が行われています。

その後、append を使用して、FizzBuzzの結果を連結しています。
```nibbles:append
:  # append  文字列連結
^-~%$3"Fizz"
^-~%$5"Buzz"

# append テスト
p :"" ""         # ""
p :"Fizz" ""     # "Fizz"
p :"Fizz" "Buzz" # "FizzBuzz"
```
ここまでの「 :^-~%\\$3"Fizz"^-~%\\$5"Buzz" 」で
3の倍数のとき Fizz
5の倍数のとき Buzz
3の倍数かつ5の倍数のとき FizzBuzz
の実装ができています。適当な入力で確かめてみてください。

続いて、FizzBuzz以外の数字出力処理を作成していきます。
if/else (lazy list) ?, を使用してFizzBuzzの結果が空文字かどうか、場合分けをしていきます。
```nibbles:if/else (lazy list)
?,  # if/else (lazy list)
:^-~%$3"Fizz"^-~%$5"Buzz"
```
演算結果は
空文字列でない -> true, 空文字列 -> false
となります。

こちらの演算は
「 ?, 条件 trueの出力 falseの出力 」
の形で記載していきます。

```nibbles:if/else (lazy list)
p ?,""     "true" "false"  # "false"
p ?,"1"    "true" "false"  # "true"
p ?,""     "true" 10       # "10"
p ?,"Fizz" $      10       # "Fizz"
```
一番下の処理、実は if/else では、「 条件 」の値が \\$ に入ります！
map のように変数の移動が起こるわけですね。
スコープは 「 trueの出力 」だけです。「 falseの出力 」までくると、参照できません。
```:ct確認
nibbles: 
Context:
LambdaArg ?,
  $  :: String 

LetArg inputs
  @ fstInt :: Integer 
  _ fstLine :: String 
  ;$ ints :: [Integer] 
  ;@ sndInt :: Integer 
  ;_ allLines :: [String] 
  ;;$ allInput :: String 
  ;;@ intMatrix :: [[Integer]] 
  ;;_ sndLine :: String 


at line: 1, char: 13
            v
p ?, "Fizz" ct
            ^
```

「 ?,:^-~%\\$3"Fizz"^-~%\\$5"Buzz" 」の解読に戻ります。
実はこれ、\$\$が最後に省略されています。
省略分を表示しながら、整理するとこんな感じになります。
```nibbles:省略前
?,  # if/else
:   # append
^-~%$3"Fizz"  # Fizz処理
^-~%$5"Buzz"  # Buzz処理
$  # true時の処理  -> FizzBuzz 文字列を表示（$:条件部分の値）
$  # false時の処理 -> 数字を表示（$:元々の数字）
```
これで、入力に対して、FizzBuzz出力を行ってくれる部分が作成できました！
```nibbles:FizzBuzzテスト
p ?,:^-~%$3"Fizz"^-~%$5"Buzz"

# 入力 出力
# 1   "1"
# 2   "2"
# 3   "Fizz"
# 5   "Buzz"
# 15  "FizzBuzz"
# 100 "Buzz"
```
最後に、1 ~ 100までの数字をmapで処理していけばOKです！
```nibbles:完成形？
.,100  # 1 ~ 100 のリストをmap
?,:^-~%$3"Fizz"^-~%$5"Buzz"

.,100?,:^-~%$3"Fizz"^-~%$5"Buzz"  # 整理
$?,:^-~%$3"Fizz"^-~%$5"Buzz"      # 公式のコード
```

なんか先頭だけ違いますね。最後に「 .,100 」と「 $ 」について見ていきます。

[こちら](https://www.golfscript.com/nibbles/tutorial_minutiae.html)のImplicit Opsの欄を再度見ていきましょう。
> These rules also apply if the first value is an integer, except that it does a "range from 1 to n" to generate a list first.

数字を先頭に書いたとき、range として扱うよって言ってます（例外アリ）。
そんなわけで、map に加えて range まで省略できるので「 .,100 」が 「 100 」となります！

最後に、「 $ 」と書ける理由についてです。
[Quick Ref](https://www.golfscript.com/nibbles/quickref.html)のInputs:欄を見てみます。
defaultという列がありますね。
対象の入力がない場合、入力の値を使用する代わりにdefaultの値が使用されるんですね。
「 $ 」のdefaultをみると、ちょうど100になっております！
そんなわけで、「 100 」が「 \\$ 」とできて、下記コードが完成します！

```nibbles:28byte
$  # 1 ~ 100 のリストをmap
?,:^-~%$3"Fizz"^-~%$5"Buzz"

$?,:^-~%$3"Fizz"^-~%$5"Buzz"  # 整理
```

# おわりに
急に難易度が爆上がりしてしまいましたが、いかがでしたでしょうか。
記事のことでもなんでも、わからない箇所があれば気軽にご質問ください。
[X](https://twitter.com/i_taku0810)宛でも構いません、可能な範囲で対応させていただきます！