Text wrapping

終わってたけど 「プログラミングGroovy」のカバーに載せる短いプログラム - Togetter みて考えたもの。
「プログラミングGROOVY」と表示するプログラム。無茶な 3 行だったのできれいにした。
本当は Groovy のロゴをどこかの APIアスキーアートに変換してコマンドプロンプトに表示したかった。


アスキーアートの生成は仲若重人氏の Java のものを Groovy に書き換えただけ。

最初に見つけたのはこれだったけど 3 行まで遠すぎるので途中でやめた。

import java.awt.*
import java.awt.image.PixelGrabber  as PG
import java.awt.image.BufferedImage as BI

"プログラミングGROOVY".each { c, w=25, h=25 ->
  new BI(w, h, BI.TYPE_INT_RGB).with { img ->
    img.createGraphics().with { g ->
      g.font = new Font(null, Font.BOLD, [w,h].min())
      g.drawString(c, 0, h)
    }
    new PG(img, 0, 0, w, h, false).with { pg ->
      pg.grabPixels()
      pg.pixels.collect{ it ? '@' : '_' }.join() =~ /.{1,$w}/
    }.each {
      sleep(50)
      println it
    }
  }
}


改行処理は最近みた問題から

Wrap the string "The quick brown fox jumps over the lazy dog. " repeated ten times to a max width of 78 chars, starting each line with "> ", yielding this result:

> The quick brown fox jumps over the lazy dog. The quick brown fox jumps
> over the lazy dog. The quick brown fox jumps over the lazy dog. The
> quick brown fox jumps over the lazy dog. The quick brown fox jumps
> over the lazy dog. The quick brown fox jumps over the lazy dog. The
> quick brown fox jumps over the lazy dog. The quick brown fox jumps
> over the lazy dog. The quick brown fox jumps over the lazy dog. The
> quick brown fox jumps over the lazy dog.


問題は 78 文字になっているが、result にあわせて 70 文字で処理した。

expected = [
  "> The quick brown fox jumps over the lazy dog. The quick brown fox jumps",
  "> over the lazy dog. The quick brown fox jumps over the lazy dog. The",
  "> quick brown fox jumps over the lazy dog. The quick brown fox jumps",
  "> over the lazy dog. The quick brown fox jumps over the lazy dog. The",
  "> quick brown fox jumps over the lazy dog. The quick brown fox jumps",
  "> over the lazy dog. The quick brown fox jumps over the lazy dog. The",
  "> quick brown fox jumps over the lazy dog.",
]
input  = "The quick brown fox jumps over the lazy dog. "

// Java, Scala, Groovy の回答だったもの
// WordUtils を使う
@Grab(group='commons-lang', module='commons-lang', version='2.6')
import org.apache.commons.lang.WordUtils
assert expected == WordUtils.wrap((input * 10).trim(), 70).readLines().collect { "> " + it }

// tokenize を使う
assert expected == (input * 10).tokenize().inject([] as LinkedList) { acc, v ->
  if (acc.empty) return acc + v
  def s = acc.removeLast()
  acc += ((s + " " + v).size() > 70) ? [s, v] : s + " " + v
}.collect { "> " + it }

// 正規表現を使う
assert expected == (input * 10 =~ /.{1,70} /).collect { "> " + it.trim() }


Groovy にはこの処理に適した String#eachMatch があるが Matcher#iterator を利用すれば他にもいろいろできる*1

groovy:000> "abc" =~ /./
===> java.util.regex.Matcher[pattern=. region=0,3 lastmatch=]

*1:Groovy では Collection でなくても iterator を返せば Object にある繰り返し処理が使えるため