「響け！ユーフォニアム」でcodegolfする

たまたま[Javascriptを1行で書く - Qiita](https://qiita.com/Butackle/items/112d61bef9fde1d3679f)という記事を見つけて、そういえばcodegolfなんてものがあったなと思い出して挑戦してみました。
[【ファン迷惑】「響け！ユーフォニアム」という文字列だけで遊ぶシェル芸人達 - Togetter](https://togetter.com/li/1041621)

この記事では`Foo.protoype.bar`は`Foo::bar`と表記します (例: `Array.prototype.join` -> `Array::join`)

# とりあえず大本のコード何個か考える
## A
```javascript
var a = "響け！ユーフォニアム".split("");
for(var i = 0; i < a.length; i++){
  console.log(a.join(""));
  a.unshift(a.pop());
}
```
Togetterのほうにあるコードですね。
`a.unshift(a.pop())`がなるほどって感じでした。

## B
```javascript
var b = "響け！ユーフォニアム";
b.forEach( (_, i) => {
  console.log(b.slice(i) + b.slice(0, i));
});
```
Togetterのほうにあるコードの二つ目です。

## C
```javascript
var b = "響け！ユーフォニアム";
[b].concat(b.split("")).reduce( (s, e) => {
  console.log(s);
  return s.slice(1)+e;
});
```
なんとなく`Array::reduce`使いたいなって思って書きました。

## D
```javascript
var a = "響け！ユーフォニアム";
for (var i in a) {
  console.log(a);
  a = a.replace(/(.)(.*)/,"$2$1");
}
```
正規表現でいくパターンです。
正直短くならないと思います。
一番直感的な気はしますが。

# 読める程度に短くする

読める程度に`var`[^1]や改行や不要なコロンを取り除いていきます。
また、`String::split`を`[...]`で書き直していきます。
同時にJavascriptでは複数のループ方法があるので、それぞれに書き直してみます。
`Array::forEach`は`Array::map`で返り値を捨てれば同じことになるのでそっちで統一します。

[^1]: `var`なしで宣言した変数はグローバル変数になる。ただし`strict`モードでは不可。

```javascript
/* ----- A ----- */
a=[..."響け！ユーフォニアム"];
// for
for(i = 0; i < a.length; i++){console.log(a.join(""));a.unshift(a.pop())}
// for in
for(i in a){console.log(a.join(""));a.unshift(a.pop())}
// map
a.map( _ => {console.log(a.join(""));a.unshift(a.pop())})

/* ----- B ----- */
b="響け！ユーフォニアム";
// for
for(i = 0; i < b.length; i++){console.log(b.slice(i) + b.slice(0, i))}
// for in
for(i in b){console.log(b.slice(i) + b.slice(0, i))}
// map
b.map( (_, i) => {console.log(b.slice(i) + b.slice(0, i))})

/* ----- C ----- */
b="響け！ユーフォニアム";
[b, ...b].reduce( (s, e) => {console.log(s);return s.slice(1)+e})

/* ----- D ----- */
b="響け！ユーフォニアム";
// for
for(i = 0; i < b.length; i++){console.log(b);b=b.replace(/(.)(.*)/,"$2$1")}
// for in
for (i in b) {console.log(b);b=b.replace(/(.)(.*)/,"$2$1")}
```
なんとなくcodegolf感が出てきました

# 関数を工夫する

同じようなことをするにも別の関数を使ったり、別の方法で使ったりすることで短くなることもあります。
少し書き換えてみます。
`for of`が先ほどないなって思った方もいらっしゃると思いますが、ここで書き直します。

## A
```javascript
a=[..."響け！ユーフォニアム"];
// for
for(i = 0; i < a.length; i++){console.log(a.join(""));a.push(a.shift())}
// for in
for(i in a){console.log(a.join(""));a.push(a.shift())}
// map
a.map( _ => {console.log(a.join(""));a.push(a.shift())})
```
回転方向は関係ない(はず)なので、`a.unshift(a.pop())`を`a.push(a.shift())`に書き換えます。
これで1文字減ります。

## D
```javascript
b="響け！ユーフォニアム";
// for
for(i = 0; i < b.length; i++){console.log(b);b=b.replace(/(.)(.*)/,"$2$1")}
// for in
for (i in b) {console.log(b);b=b.replace(/(.)(.*)/,"$2$1")}
// for of
for(s of b) {console.log(b);b=b.replace(/.(.*)/,"$1"+s)}
// for of 2
for(s of b) {console.log(b);b=b.replace(s,"")+s}
// for of 3
for(s of b) {console.log(b);b=b.slice(1)+s}
// for in 4
for(i in b) {console.log(b);/./.exec(b);b=RegExp["$'"]+RegExp["$&"]}
// for of 4
for(s of b) {console.log(b);/./.exec(b);b=RegExp["$'"]+s}
```
`for of`を使うことで`b[i]`や`$1`を`s`として利用できるので少し短くなりました。
もしかしたらと思って、`RegExp["$'"]`などの非推奨のものでも書いてみました。
正規表現が`/./`の短さになるのはメリットかなと。

```javascript
b="響け！ユーフォニアム";
for(i in b)console.log(b.replace(eval(`/(.{${i}})(.*)/`),"$2$1"));
```

というのも書いてみたんですが、長すぎますね…

# forやmapの中身の一行化
forの中身を一行にすることで`{}`を省略できます。
mapなどは内側の即時関数を`()=>{return～}`から`_=>～`にできます。
一行の場合は最後が自動で`return`されるのが大きいですね。

そこで一行への仕方ですが、何個か方法があります。

 - カンマ演算子(`,`)の利用
    - 関数の引数には使えません
    - 例: `func(1, 2)`->引数に1と2を渡すことになるからNG、`func((1, 2))`ならOK
 - 論理和/差演算子(`||`/`&&`)の利用
    - 先に来るものが必ず`true`もしくは`false`に変換できる必要があります
    - 例: `console.log()||a+b`, `a-b&&console.log()`(a-bは0にはならない場合) 
 - ビット和/差演算子(`|`/`&`)の利用
    - 先に来るものが必ず数値に変換でき`0`もしくはそれ以外に変換できる必要があります
    - また、返り値が必要な場合は使えません(数値変換されるので)
    - 例: `console.log()|a+b`

カンマ演算子が一番考えることが少ないので(たぶん)、優先したいですね。

```javascript
/* ----- A ----- */
a=[..."響け！ユーフォニアム"];
// for
for(i = 0; i < a.length; i++)console.log(a.join("")),a.push(a.shift())
// for in
for(i in a)console.log(a.join("")),a.push(a.shift())
// map
a.map( _ => console.log(a.join(""))|a.push(a.shift()))

/* ----- B ----- */
b = "響け！ユーフォニアム";
// for
for(i = 0; i < b.length; i++)console.log(b.slice(i) + b.slice(0, i))
// for in
for(i in b)console.log(b.slice(i) + b.slice(0, i))
// map
b.map( (_, i) => console.log(b.slice(i) + b.slice(0, i)))

/* ----- C ----- */
b = "響け！ユーフォニアム";
[b, ...b].reduce( (s, e) => console.log(s)||s.slice(1)+e)

/* ----- D ----- */
b = "響け！ユーフォニアム";
// for
for(i = 0; i < b.length; i++)console.log(b),b=b.replace(/(.)(.*)/,"$2$1")
// for in
for (i in b)console.log(b),b=b.replace(/(.)(.*)/,"$2$1")
// for of
for(s of b)console.log(b),b=b.replace(/.(.*)/,"$1"+s)
// for of 2
for(s of b)console.log(b),b=b.replace(s,"")+s
// for of 3
for(s of b)console.log(b),b=b.slice(1)+s
// for in 4
for(i in b)console.log(b),/./.exec(b),b=RegExp["$'"]+RegExp["$&"]
// for of 4
for(s of b)console.log(b),/./.exec(b),b=RegExp["$'"]+s
```
だいぶ短くなってきましたね。

# ()などの除去
テンプレートレテラルを用いて`func("文字列")`は``` func`文字列` ```に書き換えます。
空白の完全除去も同時に行います。

```javascript
/* ----- A ----- */
a=[..."響け！ユーフォニアム"];
// for
for(i=0;i<a.length;i++)console.log(a.join``),a.push(a.shift())
// for in
for(i in a)console.log(a.join``),a.push(a.shift())
// map
a.map(_=>console.log(a.join``)|a.push(a.shift()))

/* ----- B ----- */
b="響け！ユーフォニアム";
// for
for(i=0;i<b.length;i++)console.log(b.slice(i)+b.slice(0,i))
// for in
for(i in b)console.log(b.slice(i)+b.slice(0,i))

/* ----- C ----- */
b="響け！ユーフォニアム";
[b,...b].reduce((s,e)=>console.log(s)||s.slice(1)+e)

/* ----- D ----- */
b="響け！ユーフォニアム";
// for of
for(s of b)console.log(b),b=b.replace(/.(.*)/,"$1"+s)
// for of 2
for(s of b)console.log(b),b=b.replace(s,"")+s
// for of 3
for(s of b)console.log(b),b=b.slice(1)+s
// for of 4
for(s of b)console.log(b),/./.exec(b),b=RegExp["$'"]+s
```

いくつか明らかに無理そうなのは削除しました。(`for in`で`i`を使っていなかったり`for of`で`s`を使っていなかったり)

# 変数代入やインクリメントの移動
例としてAの`for`のバージョンを使います。

```javascript
b="響け！ユーフォニアム";
for(i=0;i<b.length;i++)console.log(b.slice(i)+b.slice(0,i))
```

`i`を上へ`a.length`に向かってインクリメントしてますが、`a.length`から始めてデクリメントするようにします。

```javascript
b="響け！ユーフォニアム";
for(i=b.length;i;i--)console.log(b.slice(i)+b.slice(0,i))
```

こうすると、終了条件が`i`になって2文字減ります。[^2]

[^2]: `i`が`false`のときに`for`抜ける->`i`が`0`(変換すると`false`)のときに抜ける

```javascript
b="響け！ユーフォニアム";
for(i=b.length;i;)console.log(b.slice(i)+b.slice(0,i--))
```

インクリメントもfor文内に突っ込むとさらに1文字減ります。
ほかの文にも同じようにしていきます。

```javascript
/* ----- A ----- */
a=[..."響け！ユーフォニアム"];
// for
for(i=a.length;i;i--)console.log(a.join``),a.push(a.shift())
// for in
for(i in a)console.log(a.join``),a.push(a.shift())
// map
a.map(_=>console.log(a.join``)|a.push(a.shift()))

/* ----- B ----- */
b="響け！ユーフォニアム";
// for
for(i=b.length;i;)console.log(b.slice(i)+b.slice(0,i--))
// for in
for(i in b)console.log(b.slice(i)+b.slice(0,i))

/* ----- C ----- */
[b="響け！ユーフォニアム",...b].reduce((s,e)=>console.log(s)||s.slice(1)+e)

/* ----- D ----- */
b="ム響け！ユーフォニア";
// for
for(i=b.length;i;i--)console.log(b=b.replace(/(.)(.*)/,"$2$1"))
// for of
for(s of b)console.log(b=b.replace(/.(.*)/,"$1"+s))
// for of 2
for(s of b)console.log(b=b.replace(s,"")+s)
// for of 3
for(s of b)console.log(b=b.slice(1)+s)
// for of 4
for(s of b)/./.exec(b),console.log(b=RegExp["$'"]+s)
```

`"ム響け！ユーフォニア"`というのはせこい気もしますが…

# 結果

```javascript
/* ----- A ----- */
a=[..."響け！ユーフォニアム"];a.map(_=>console.log(a.join``)|a.push(a.shift()))
/* ----- B ----- */
for(i in b="響け！ユーフォニアム")console.log(b.slice(i)+b.slice(0,i))
/* ----- C ----- */
[b="響け！ユーフォニアム",...b].reduce((s,e)=>console.log(s)||s.slice(1)+e)
/* ----- D ----- */
for(s of b="ム響け！ユーフォニア")console.log(b=b.slice(1)+s)
```

ということで自分の最短は

```javascript
for(s of b="ム響け！ユーフォニア")console.log(b=b.slice(1)+s)
```

の51文字でした

ちなみにCoffeeScriptだと

```coffeescript
console.log b=b[1..]+s for s in b="ム響け！ユーフォニア"
```
の46文字でした

# 参考
[Tips for golfing in JavaScript - StackExchange](https://codegolf.stackexchange.com/questions/2682/tips-for-golfing-in-javascript)
[Tips for Golfing in ECMAScript 6 and above - StackExchange](https://codegolf.stackexchange.com/questions/37624/tips-for-golfing-in-ecmascript-6-and-above)