LT 大会の発表順決定に使える! ランダムに並べ替えるコード・ツールのコードゴルフ選手権

Clojure,Elixir,Groovy,Java,Kotlin,Lightning Talks,Node.js,Perl,PHP,PowerShell,Python,Ruby,Scala,SQL,プログラミング,社内勉強会

こんにちは、ソリューション開発部の柴崎です。

最近、まじめな記事ばかり投稿してしまったので、ゆるい記事を書きます。

さて、複数人から無作為に発表順を決めるなど、ランダムに並べ替えたいことはよくあると思います。そんなランダムに並べ替える各種言語やツールに対して、コードゴルフ的に挑戦します。LT 大会で発表順を決めたいなど言われたら、これでドヤ顔しながらコーナーで差をつけろ!!

ルール説明

  • 適当な数人をランダムに並べ替えるコードの短さを競います。
    • 人数と名前は可変とします。
    • 今回の例では、佐藤, 鈴木, 高橋, 田中, 伊藤の5人にします。
    • 出力結果を見て順番が分かれば OK とします。
  • コマンドラインで実行できるワンライナー限定です。
    • 各種ツールの2020年2月時点での最新かそれに近いバージョンで実行します。
    • Windows のコマンドプロンプトでは動かないものがあるかも (未検証) 🙇🙇🙇
  • レギュレーションが緩いって?
    • こんな きじ に まじ に なっちゃって どうするの

コードゴルフに挑戦

思いついた言語やコマンドラインでコードゴルフに挑戦していきます。所要文字数が多い順に発表します。果たして最も短い方法は何でしょうか……!?

Java(116文字)

$ echo 'Stream.of("佐藤","鈴木","高橋","田中","伊藤").sorted((a,b)->(int)(Math.random()*3)-1).forEach(System.out::print)'|jshell
|  JShellへようこそ -- バージョン11.0.6
|  概要については、次を入力してください: /help intro

jshell> Stream.of("佐藤","鈴木","高橋","田中","伊藤").sorted((a,b)->(int)(Math.random()*3)-1).forEach(System.out::print)
高橋鈴木伊藤田中佐藤
jshell> 

jshell コマンドのヘルプを見れば分かる通り、load-file- を渡すことで対話型I/Oにせず標準入力から入力を受けとるとあるのですが、指定しなくてもなんか出てくれました。ヤッター!

出力結果の名前と名前の間に区切りがない? LT 前のネタなんだから、つながっているくらいがちょうど良いんですよ。print の代わりに println を使うことで2文字と引き換えに改行区切りで出力できます。

Python(81文字)

$ python3 -c"import random as r;a=['佐藤','鈴木','高橋','田中','伊藤'];r.shuffle(a);print(a)"
['鈴木', '佐藤', '伊藤', '高橋', '田中']

もう少し短くできそうな気がするのですが、私のスキルではこれが限界でした。正統派なコードを感じます。オプション直後のスペースを省略してしっかり刻んでいきます。

Node.js(76文字)

$ node -e "console.log(['佐藤','鈴木','高橋','田中','伊藤'].sort(()=>Math.random()-.5))"
[ '高橋', '鈴木', '田中', '伊藤', '佐藤' ]

console.log が足を引っ張りました。ランダムな±の数値で並べ替えを実現しています。

psql コマンド(72文字)

$ psql -c"SELECT*FROM UNNEST('{佐藤,鈴木,高橋,田中,伊藤}'::TEXT[])ORDER BY RANDOM()"
 unnest 
--------
 高橋
 伊藤
 鈴木
 田中
 佐藤
(5 rows)

どの DBMS でもよかったのですが、コマンドラインツールの名前が短い psql を採用しました。

パラメーターを省略するためにローカルにサーバーを立てたり、ユーザー名と同じ名前のユーザーとデータベースをサーバーに事前に作っておく必要があるので邪道感がありつつも、UNION を使わずにできたあたりを褒めてあげたい。ARRAY コンストラクタを使うよりも ::text[] としたほうが引用符分を短縮できますが、普段は ARRAY を使った方が見やすいのでそちらを使いましょう。

Perl(68文字)

$ perl -e"use List::Util 'shuffle';print shuffle(qw(佐藤 鈴木 高橋 田中 伊藤));"
高橋佐藤伊藤田中鈴木

モジュールの指定が足を引っ張りました。qw で引用符を削れたのはなかなか良かったと思います。

出力結果の名前と名前の間に区切りがない? 順番が分かるからレギュレーション的に許容っ……! 圧倒的許容っ……! print の前に $,=','; 入れることで、7文字と引き換えにカンマ区切りで出力できます。

Scala(64文字)

$ scala -e "print(util.Random.shuffle(List('佐藤,'鈴木,'高橋,'田中,'伊藤)))"
warning: there were 5 deprecation warnings (since 2.13.0); re-run with -deprecation for details
List('鈴木, '田中, '佐藤, '伊藤, '高橋)

文字列をやめてシンボルにすることで閉じ引用符分を短縮できますが、警告が出ます。そんなコードを書いてはいけません。

Groovy(64文字)

$ groovy -e"print(['佐藤','鈴木','高橋','田中','伊藤'].sort{Math.random()})"
[鈴木, 田中, 伊藤, 佐藤, 高橋]

Collections.shuffle を使うなんて長すぎます!

PHP(62文字)

$ php -r'$a=["佐藤","鈴木","高橋","田中","伊藤"];shuffle($a);print_r($a);'
Array
(
    [0] => 伊藤
    [1] => 佐藤
    [2] => 田中
    [3] => 高橋
    [4] => 鈴木
)

変数に $ が付く文化圏のコードをコマンドラインで実行する場合は、変数が展開されないように注意しましょう! (数分、ハマったのは内緒)

Kotlin(58文字)

$ echo 'listOf("佐藤","鈴木","高橋","田中","伊藤").shuffled()'|kotlinc
Welcome to Kotlin version 1.3.61 (JRE 11.0.6+10)
Type :help for help, :quit for quit
>>> listOf("佐藤","鈴木","高橋","田中","伊藤").shuffled()res0: kotlin.collections.List<kotlin.String> = [鈴木, 佐藤, 田中, 伊藤, 高橋]
>>> 

なりふり構わず、標準出力に出すコードを省略しました。REPL は便利だなあ。

Elixir(57文字)

$ elixir -e "IO.inspect Enum.shuffle [:佐藤,:鈴木,:高橋,:田中,:伊藤]"
[:鈴木, :伊藤, :田中, :佐藤, :高橋]

何かこう、正統派な可もなく不可もなくな感じ。

文字列をやめてアトムにすることで閉じ引用符分を短縮できますが(以下略)

Groovy(55文字)

$ groovy -e"print(['佐藤','鈴木','高橋','田中','伊藤'].shuffled())"
[伊藤, 佐藤, 鈴木, 田中, 高橋]

さっき64文字といったな。あれは嘘だ。バージョン 3 以降なら shuffled メソッドが使えます。

タイムリーなので布教しときます。バージョン 3 でようやく Stream も扱いやすくなりました。以下の様に書けるようになったので、Stream の対応で Groovy の評価を下げていた方は、これを機にぜひ再評価を。

$ groovy -e "[1, 2, 3].stream().filter(i -> i % 2 == 1).each(System.out::println)"
1
3

PowerShell(53-54文字)

Mac 環境 pwsh コマンド (54文字)

$ pwsh -c "'佐藤','鈴木','高橋','田中','伊藤'|Sort-Object{random}"
伊藤
田中
高橋
佐藤
鈴木

powershell コマンドより文字数を稼げると思いきや、Sort-Objectsort エイリアスがなく、余計に文字数を使うことになりました。残念。

Windows 環境 powershell コマンド(53文字)

$ powershell -c "'佐藤','鈴木','高橋','田中','伊藤'|sort{random}"
伊藤
佐藤
田中
高橋
鈴木

対して Windows 環境の powershell コマンドですが、思ったより短く書けました。

コマンドのエイリアスの有無が異なっているものの、Mac でも Linux でも動くようになった PowerShell は、これから有力な選択肢になるかと思います。

Clojure(47文字)

$ echo '(shuffle[:佐藤,:鈴木,:高橋,:田中,:伊藤])'|lein repl
nREPL server started on port 62457 on host 127.0.0.1 - nrepl://127.0.0.1:62457
REPL-y 0.4.3, nREPL 0.6.0
Clojure 1.10.0
OpenJDK 64-Bit Server VM 11.0.6+10
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (shuffle[:佐藤,:鈴木,:高橋,:田中,:伊藤])
[:田中 :高橋 :佐藤 :伊藤 :鈴木]
user=> Bye for now!

clj コマンド? いえ、知らない子ですね。こちらも REPL のおかげで標準出力に出すコードを省略しています。

文字列をやめてキーワードにすることで閉じ引用符分を短縮できますが(以下略)

Ruby(40文字)

$ ruby -e"p [:佐藤,:鈴木,:高橋,:田中,:伊藤].shuffle"
[:伊藤, :高橋, :鈴木, :田中, :佐藤]

さすがの見やすさ、王者の貫禄を感じます。

文字列をやめてシンボルにすることで閉じ引用符分を短縮できますが(以下略)

sort コマンド(35文字)

$ printf '佐藤\n鈴木\n高橋\n田中\n伊藤'|sort -R
伊藤
佐藤
鈴木
田中
高橋

ヒアドキュメントを書きそうになりましたが、ワンライナーでの改行は邪道でしょう。

shuf コマンド(22文字)

$ shuf -e 佐藤 鈴木 高橋 田中 伊藤
田中
鈴木
高橋
伊藤
佐藤

優勝。もうこれ以上はない!

いかがでしたか

なんと、普段は書かない言語も知ったかぶりで書いている模様です!
コードゴルフは奥が深いですね……

皆さんも LT 大会の発表順決定に使える手法の最短記述にチャレンジしてみてください!

  • 株式会社アークシステムの来訪管理・会議室予約システム BRoomHubs
  • 低コスト・短納期で提供するまるごとおまかせZabbix