uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

括弧を減らせ…括弧ゴルフwith Groovy:結果0個

旧聞に属しますが、ちょっと前に流行った括弧ゴルフ(括弧をなるべく使わずに階乗を計算するやつ)をgroovyでやってみました。

a = args as List
a += '1'
a.putAt 1, 'x'
a -= 'x'
s = a as String
c = s.chars
c.putAt 0, " "
minus1 = -1
c.putAt minus1, " "
t = c as String
n = t as int
r = 1..n
acc = 1G
r.each {
  acc *= it
  println it + "! = " + acc
}

2個です。階乗の値を計算する部分は、ループ(for/while文)にせよ、再帰するにせよ、括弧使わずには書けないから、これが限界かな?

何しろとにかく、

  • 値を返すメソッド呼び出しは使えない
    • 値を返してもそれを受け取ろうとするとトップレベルにならなくなるので引数の括弧が省略できない
  • 配列の要素参照は使えない
  • if文やwhile文は使えない
  • 3項演算子は、演算子の優先度の関係で,多くの場合使えない
  • クロージャ生成ができない。
    • 他から.&で持ってきたクロージャをmopでinjectして演算子経由で呼び出すとかはありか(思いつかないけど)。

ので、残る

  • 副作用のあるメソッドのトップレベルでの呼び出し
  • その他演算子
  • 代入
  • プロパティ参照

のみで書く必要があるわけです。

gamma関数を@Grabで取ってきたり、List#permutations()の結果リストの長さをはかる、なども考えたけど、値を返すメソッド呼び出しである以上、括弧は要るわけだし。

(追記)コメント欄で id:murky-satyr さんに教えていただきましたが、Evalを使ってよいなら、0個にできます。できないと思ってたループは、コード断片を掛け算で個数分に増やすと。その発想はないわー。ありがとうございました> id:murky-satyr さん

a = args as List
a += '1'
a.putAt 1, 'x'
a -= 'x'
s = a as String
c = s.chars
c.putAt 0, " "
minus1 = -1
c.putAt minus1, " "
t = c as String
n = t as int
Eval.xy 1, 1g, 'println "$x! = $y"; y *= ++x;'* n

何の言語だ、ぐらいな感じですね。

(追記)コメント欄で春風さんに指摘いただきましたが、いろいとやってたのを、LinkedListにasした上でLinkedList#getFirst()を使うことで圧倒的に短くなりました。GDKメソッド以外も探す必要があったというわけですが、他にもあるかな。ありがとうございます>春風さん

a = args as LinkedList
a += '1'
n = a.first as int
Eval.xy 1, 1g, 'println "$x! = $y"; y *= ++x;'* n

(追記) コメント欄で id:murky-satyr さんより、もっと短くなる方法を教えてもらいました! (あと引数を省略した場合の対応をちょっとだけ入れました)

n = "$args 1" - ~/\D+/ - ~/\D.*/ as int
Eval.xy 1, 1g, 'println "$x! = $y"; y *= ++x;'* n

おお!これはすごい。
String#minus(Pattern)でパターンが表す正規表現にマッチした文字列を削除できるのかー。
replaceAllを使わなくても済ませられる時がありそうだ。これは実用的だ。