文字列で+を表示する

Groovyのtrampolineを使ってピラミッドのお題を解いてみた - No Programming, No Life が楽しそうなので私も エロと風俗情報満載 どう抜く? から引用する。


pooq 氏からの お題

与えられた文字列で+のかたちを表示するプログラムをかいてください。
サンプル入力:
doukaku

サンプル出力:
       doukakud
       u      o
       k      u
       a      k
       k      a
       u      k
       o      u
doukakud      doukakud
u                    o
k                    u
a                    k
k                    a
u                    k
o                    u
dukakuod      dukakuod
       u      o
       k      u
       a      k
       k      a
       u      k
       o      u
       dukakuod


プログラミング自体が好きなので面白いお題を出してくれる人や自分では気づけないようなコードを書いてくれる人は私にとってヒーローだ。Scala ヒーローや Haskell ヒーローのコードを読みたいから新しい言語を勉強しているという面もある。
私が思いついたのはポピュラーな一筆書きの方法だったが他の方法で回答されているヒーローのコードを Groovy に翻訳してみた。

// http://ja.doukaku.org/291/
// @author pooq
// 与えられた文字列で+のかたちを表示するプログラムをかいてください。


// http://ja.doukaku.org/comment/10085/
// @author 匿名
def solve1(s) {
  def res = []
  def len = s.size()
  def rev = s.reverse()
  // 文字列の幅で 0, 1, 2, 3 倍の4つのラインを考える
  def (l0, l1, l2, l3) = [0, len, len*2, len*3]
  for (r in 0..len*3) {
    res << []
    for (c in 0..len*3) {
      // 中央部分かの判定フラグ
      def (cc,rr) = [c > l1 && c < l2, r > l1 && r < l2]
      // 出力方向: 0,1 or 2,3
      // 出力判定: 中央 or 中央以外
      res.last() << (r == l0 &&  cc || r == l1 && !cc ? s[c % len] :       // 0, 1 行目は右方向
                     r == l2 && !cc || r == l3 &&  cc ? rev[(c-1) % len] : // 2, 3 行目は左方向
                     c == l0 &&  rr || c == l1 && !rr ? rev[(r-1) % len] : // 0, 1 列目上方向
                     c == l2 && !rr || c == l3 &&  rr ? s[r % len] :       // 2, 3 列目下方向
                     ' ')
    }
  }
  res*.join().join('\n')
}

// http://ja.doukaku.org/comment/10105/
// @author kozima
def solve2(s) {
  def rotate = { it.reverse().transpose() }
  def merge  = { lines -> lines.transpose()*.findResult(' ') { if (it != ' ') it } }
  def n = s.size()
  def e = ' ' * n
  def empty = "$e$e$e "
  def line1 = "$e$s$e "
  def line2 = "$s$e$s "
  def m  = [line1, *([empty] * (n-1)), line2, *([empty] * 2*n)]*.toList()
  def m1 = rotate(m)
  def m2 = rotate(m1)
  def m3 = rotate(m2)
  [m,m1,m2,m3].transpose().collect { /* println it*.join(); */ merge(it).join() }.join('\n') 
}

def (s) = args ?: ["doukaku"]
println solve1(s)
println solve2(s)
solve1

匿名氏の Java での回答
十字は縦横4本のラインからどの部分を表示するかという問題に置き換わっている。
単純に2重ループしているだけなのに表示できてしまうのがすごい。
Java から Groovy だとそのままでも動作するということになりそうなので文字列を返すように変更してみた*1


solve2

kozima氏の Common Lisp での回答
問題を見てから図形の組合せにしようとするまでの判断が知りたい。
同じ向きの文字を抽出したのだろうか?

コメントをはずすと m,m1,m2,m3 を表示できる。これを重ねあわせている。

[       doukaku        ,               d       ,                       ,                       ]
[                      ,               o       ,                       ,        u              ]
[                      ,               u       ,                       ,        k              ]
[                      ,               k       ,                       ,        a              ]
[                      ,               a       ,                       ,        k              ]
[                      ,               k       ,                       ,        u              ]
[                      ,               u       ,                       ,        o              ]
[doukaku       doukaku ,                      d,                       ,        d              ]
[                      ,                      o,                       , u                     ]
[                      ,                      u,                       , k                     ]
[                      ,                      k,                       , a                     ]
[                      ,                      a,                       , k                     ]
[                      ,                      k,                       , u                     ]
[                      ,                      u,                       , o                     ]
[                      ,               d       ,  ukakuod       ukakuod, d                     ]
[                      ,               o       ,                       ,        u              ]
[                      ,               u       ,                       ,        k              ]
[                      ,               k       ,                       ,        a              ]
[                      ,               a       ,                       ,        k              ]
[                      ,               k       ,                       ,        u              ]
[                      ,               u       ,                       ,        o              ]
[                      ,                       ,         ukakuod       ,        d              ]

Common Lisp の関数が分からないのでそのままではないだろうが近い感じに Groovy で記述することができた*2
90度の rotate が上下反転 + 行と列の入れ替えというのも勉強になった。

*1:Groovy は負のインデックスで後ろから参照できるのでそのあたりも

*2:Ruby が Matz Lisp と呼ばれる所以は、内部イテレータにあるってこと?