Java8ワンライナーで彼女の好感度をMaxにする

More than 3 years have passed since last update.

後輩がPythonでワンライナーやってたのでそのオマージュです。

POH!7(paiza Online Hackathon 7)の問題を全てJava8のStream APIで解いて、

好感度の上がる問題はほぼ全てワンライナーで書いたのでそのまとめです。

めがねとサンタ服と水着も載せています。

マサカリを投げられることを期待しています。

(12月14日追記) 眼帯と縞ニーソを追加しました。

(3月29日追記) 猫耳、猫セット、メイド服を追加しました。


はじめに

Javaってコードが冗長になりがちって思ってませんか? まあそうなんですけど。

そういう既存の概念を壊したくてこの記事を書きました。

ぶっちゃけコマンドラインからの入力の取得込みで1行で書ける仕様にJavaがなっていなんじゃなかなって書きながら思ってました。

始めに言っておくと、ちょっとズルしてます。Javaマスター助けてください。

記事書いた後に思いついて解決しました。



つり目


Main.java

System.out.println(new BufferedReader(new InputStreamReader(System.in)).lines().limit(1)

.flatMap(s->IntStream.range(0, Integer.valueOf(s)).mapToObj(i->"Ann")).map(String::valueOf)
.collect(Collectors.joining()));

どっかの問題で別の書き方してたかも知れないですがここでは読み込んだ数でrange作ってAnnのListにしてflatMapで追加する方式を採用しています。


眼帯


Main.java

System.out.println(Stream.of(Stream.of(

new BufferedReader(new InputStreamReader(System.in)).lines().limit(5)
.collect(Collectors.joining("/"))).map(s->s.split("/")).flatMap(s->Arrays.stream(s[4].split(" "))
.filter(n->Arrays.stream(s[2].split(" ")).noneMatch(m->m.equals(n))))
.mapToInt(o->Integer.valueOf(o.toString())).sorted().mapToObj(String::valueOf)
.collect(Collectors.joining(" "))).filter(s->!s.isEmpty()).findFirst().orElse("None"));

Streamの処理は一部だけ処理するとか順番を考慮するとかできないので死ぬかと思いました。

やってることは売ってる本から持ってる本の差分を取ってるだけなので、コードの本質的な部分はfilterだけです。

もうちょっとまわりくどくない方法ないんだろうか。


アクセサリー


猫耳


Main.java

System.out.println(new BufferedReader(new InputStreamReader(System.in)).lines().limit(1)

.flatMap(s->Arrays.stream(s.replaceAll("cat", "/_/").split("_"))).count() - 1);

catでsplitしたときcatcatcatだと(置換しないと)countが0になるぅーって思ってました。

あとどうでもいい身内ネタだけどyukicoderで似た問題見た。


猫セット


Main.java

System.out.println(new BufferedReader(new InputStreamReader(System.in)).lines().limit(1)

.map(s->s.chars().mapToObj(c->Character.toString((char)c)).collect(Collectors.joining(" ")))
.map(s->Stream.of(
Arrays.stream(s.split(" ")).filter(c->c.equals("c")).count(),
Arrays.stream(s.split(" ")).filter(c->c.equals("a")).count(),
Arrays.stream(s.split(" ")).filter(c->c.equals("t")).count()).map(l->l.toString()).collect(Collectors.joining(" ")))
.flatMapToInt(s->IntStream.concat(
IntStream.of(Arrays.stream(s.split(" ")).mapToInt(Integer::valueOf).min().getAsInt()),
Arrays.stream(Arrays.stream(s.split(" ")).mapToInt(Integer::valueOf)
.map(i->Arrays.stream(s.split(" ")).mapToInt(Integer::valueOf).max().getAsInt() - i).toArray()))
).mapToObj(String::valueOf).collect(Collectors.joining("\n")));

こんなんワンライナーでやらせんなよ死ぬわ(本音)。

はい、かなりズルっぽいことしました。Collectors.joining最高です。

'c','a','t'のそれぞれの数を出すためにcharにバラしてフィルターしてカウントしてます。カウントしたものを空白で結合してるのはmin,maxを出したいからですがクソ回りくどいのがクソ&クソと思っています。

あとこれもyukicoderで似たようなの見た。



ショートヘア


Main.java

System.out.println(new BufferedReader(new InputStreamReader(System.in)).lines().limit(2)

.mapToInt(Integer::valueOf).sum());

IntStream強い以外の感想がない。


ロングヘアー


Main.java

System.out.println(new BufferedReader(new InputStreamReader(System.in)).lines().limit(5)

.collect(Collectors.groupingBy(s->s, Collectors.mapping(s->s, Collectors.toList()))).entrySet().stream()
.max((e1,e2)->Integer.compare(e1.getValue().size(), e2.getValue().size())).get().getKey());

型intに変えちゃうとyesかnoかわからなくなるのでMap>にして比較しています。


ポニーテール


Main.java

System.out.println(new BufferedReader(new InputStreamReader(System.in)).lines().limit(1)

.flatMap(s->IntStream.rangeClosed(1, Integer.valueOf(s))
.boxed().sorted(Comparator.reverseOrder())).map(String::valueOf).collect(Collectors.joining("\n")) + "\n0!!");

IntStream強いってさっき言いましたが、昇順にしかできねぇのかよぉ!って思ったのでやっぱ強くないです。

どうやら(IntStreamオブジェクト).boxed().sorted(Comparator.reverseOrder())するしかないってstackoverflowでも言われてました。


ツインテール


Main.java

System.out.println(Arrays.stream(new BufferedReader(new InputStreamReader(System.in)).lines().limit(2)

.collect(Collectors.joining("_2 ", "1 ", "")).split("_"))
.map(s->Arrays.stream(s.toString().split(" ")).mapToDouble(Double::valueOf).toArray())
.max((n1,n2)->Double.compare(n1[1] / n1[2], n2[1] / n2[2])).map(d->(int)d[0]).get());

どっちが1でどっちが2なのかわからなくなるので、ドリンクが2つしかないことをいいことにjoining芸(デリミタ, 最初に付ける文字, 最後に付ける文字)かましました。



セーラー服


Main.java

System.out.println(Stream.of(new BufferedReader(new InputStreamReader(System.in)))

.flatMap(br->Stream.of(br.lines().limit(1).findFirst().get()).flatMap(s->br.lines().limit(Integer.valueOf(s))))
.map(o->o.toString()).collect(Collectors.joining("_")));

最大の鬼門1行目に何行読み込むか書いてあるやつ。

brはどこにあるかって?private staticなMainクラスのフィールドです。この記事で一番のマサカリどころです!!

インスタンス作りなおすとストリームの内容保持できないので許してください。


嘘です。BufferedReaderをStreamにしたらできました。


カーディガン


Main.java

System.out.println(IntStream.rangeClosed(2, Integer.valueOf(

new BufferedReader(new InputStreamReader(System.in)).readLine())).reduce(1, (i,j)->i*j));

IntStreamにsum()ってあるならmulti()ってあってもいいと思いませんか?

まあ、ないからreduceで作ったんですけど。

reduce使えば掛け算と足し算は何とでもできる気はしますけどそれでもreduce(1, (i,j)->i*j))を置き換えるmulti()はほしい。


縞ニーソ


Main.java

System.out.println(Stream.of(new BufferedReader(new InputStreamReader(System.in)).lines().limit(2)

.collect(Collectors.joining("/"))).map(s->Arrays.stream(s.split("/")).mapToInt(Integer::valueOf).toArray())
.map(n->Arrays.stream(IntStream.range(0, n[1] / (n[0] * 2) + 1).mapToObj(i->"R W")
.collect(Collectors.joining(" ")).split(" ")).flatMap(s->IntStream.range(0, n[0]).mapToObj(i->s))
.map(o->o.toString()).limit(n[1]).collect(Collectors.joining())).findFirst().get());

眼帯と同じ手法使いました。2行の引数を1つの配列で扱えれば勝ったも同然です。

"R W"をm/(n*2)+1だけ用意してそれぞれn倍に増やしてm個で切りました。


メイド服


Main.java

System.out.println(Stream.of(new BufferedReader(new InputStreamReader(System.in)))

.flatMap(br->Stream.of(br.lines().limit(1).findFirst().get()).flatMap(s->br.lines().limit(Integer.valueOf(s))))
.mapToInt(o->Integer.valueOf(o.toString())).map(i-> i / 3).map(i->i > 60 ? 60 * 25 - i : 60 - i)
.mapToObj(i->String.format("%02d:%02d", i / 60, i % 60)).collect(Collectors.joining("\n")));

セーラー服と同じような手を使った以外に改めて特に何か言うことはないです。

どうでもいいけど、7時に起きた時点で仕事始まってるのヤバくないですか?


おまけ

ここからはワンライナーをやめて黒歴史黒魔術を行使しました。


めがね


Main.java

private static List<String> trimReadLine(BufferedReader br, int limit) {

return br.lines().limit(limit).map(s->Arrays.stream(s.split(" ")).collect(Collectors.joining())).collect(Collectors.toList());
}

private static List<String> reverseList(List<String> list) {
return IntStream.range(0, list.size()).mapToObj(i->list.stream().map(s->s.substring(i, i + 1)).collect(Collectors.joining()))
.collect(Collectors.toList());
}

private static List<String> guess(List<String> l1, List<String> l2) {
return IntStream.range(0, l1.size()).filter(i->l1.get(i).contains(l2.get(0)))
.filter(i->i + l2.size() <= l1.size()).boxed().map(i->i.toString()).collect(Collectors.toList());
}



Main.java

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

List<String> l1 = trimReadLine(br, Integer.valueOf(br.readLine()));
List<String> l2 = trimReadLine(br, Integer.valueOf(br.readLine()));
guess(l1, l2).stream().flatMap(y->guess(reverseList(l1), reverseList(l2)).stream().map(x->y + " " + x))
.map(s->Arrays.stream(s.toString().split(" ")).mapToInt(Integer::valueOf).toArray()).filter(n->
IntStream.range(n[0], n[0] + l2.size()).allMatch(i->l2.get(i - n[0]).equals(l1.get(i).substring(n[1], n[1] + l2.size()))))
.map(n->n[0] + " " + n[1]).forEach(System.out::println);

String.contains芸で当てはまりそうなx,yの座標をそれぞれ出して全ての組み合わせ作って実際に照合してみる感じですね。

よく見たらこれだけ変な出力の仕方してますね。


サンタ服


Main.java

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

String[] arg0 = br.readLine().split(" ");
System.out.println(Stream.concat(Stream.of("0 " + arg0[0], "0 0", "1 " + arg0[1], "1 0"),
br.lines().limit(Integer.valueOf(arg0[3]))).map(s->s.split(" "))
.collect(Collectors.groupingBy(s->s[0], Collectors.mapping(s->s[1], Collectors.toList()))).entrySet().stream()
.map(e->e.getValue().stream().mapToInt(Integer::valueOf).sorted().boxed().collect(Collectors.toList()))
.mapToInt(values->IntStream.range(1, values.size()).map(i->values.get(i) - values.get(i - 1)).min().getAsInt())
.reduce(Integer.valueOf(arg0[2]), (i,j)->i*j));

縦と横で同じ操作できるかなって思ったので、d_iをキーにグルーピングしてソートして切れ目毎の長さが一番短いものを出してきてreduce芸かましました。

やっぱIntStream強くないですか?


水着


Main.java

private static long cutNum(String n, int lim) {

return n.length() < lim ? Long.valueOf(n) : Long.valueOf(n.substring(n.length() - lim));
}

private static long calc(long n) {
long c = n;
while(c % 10 == 0) c /= 10;
return cutNum(String.valueOf(c), 11);
}



Main.java

System.out.println((int) cutNum(String.valueOf(new BufferedReader(new InputStreamReader(System.in))

.lines().limit(1).mapToLong(Long::valueOf).flatMap(n->IntStream.rangeClosed(2, (int)n).mapToLong(i->(long)i))
.reduce(1, (i,j)->calc(i*j))), 9));

実はこれ一番最初にやりました。しかもpythonで。

ツインテールの応用版みたいな感じがします。

whileをStreamで書く方法教えて下さい。


さいごに

Javaでムダに長いワンライナーを書くと闇が生まれるので程々にするべきだと思います。