わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

'*'で描画しよう(2)

昨日の答えです.

1. シェルで以下のコマンドを実行したら,何が表示されますか?

やってみますと,

$ ruby -e 'n=13;n.times{|i| puts " "*(i-n/2).abs + "*"*(n-2*(i-n/2).abs)};'
      *
     ***
    *****
   *******
  *********
 ***********
*************
 ***********
  *********
   *******
    *****
     ***
      *

「13」を変えてみましょう.

$ ruby -e 'n=7;n.times{|i| puts " "*(i-n/2).abs + "*"*(n-2*(i-n/2).abs)};'
   *
  ***
 *****
*******
 *****
  ***
   *
$ ruby -e '3.upto(9){|n|n.times{|i| puts " "*(i-n/2).abs + "*"*(n-2*(i-n/2).abs)}}'
 *
***
 *
  
 **
****
 **
  *
 ***
*****
 ***
  *
   
  **
 ****
******
 ****
  **
   *
  ***
 *****
*******
 *****
  ***
   *
    
   **
  ****
 ******
********
 ******
  ****
   **
    *
   ***
  *****
 *******
*********
 *******
  *****
   ***
    *

おおむね,'*'を並べてダイヤモンド型にしていることがわかります.
しかし空行があったりなかったりします.よく見ると,n=4,6,8のときの先頭のようです.
それでワンライナーを見直すと,nが偶数でiが0のとき*1は,(i-n/2).absはn/2,(n-2*(i-n/2).abs)は0となってしまい,空行に見える行が出るというわけですね.

2. このプログラムを改良してください.

方針としては,

でやってみましょう.偶数のとき,ダイヤモンド型にどうしてもならないのですが*2,まあこれはこの図形を出すということにします.
それで,コードです.

#!/usr/bin/env ruby

# diamond.rb

class Diamond
  def initialize(n_ = 0)
    @n = n_.to_i
    @n = 13 if @n <= 0
  end

  def start
    @n.times do |i|
      spc = (i - @n / 2).abs
      ast = @n - 2 * spc
      next if ast == 0
      puts " " * spc + "*" * ast
    end
  end
end

if __FILE__ == $0
  Diamond.new(ARGV.shift).start
end

そういえば,ワンライナーでは見えていませんでしたが,' 'の個数を求めるときにも,'*'の個数を求めるときにも,「(i-n/2).abs」という式が共通して現れているのですね.2回書くのは当然ながら保守性を損ないますので,この式の値をいったん変数に入れて,参照するようにしました.
initializeメソッドの引数n_について,初期値に0を入れていますが,nilでもかまいません.ここを13とするのは…2度,デフォルトの値を書くのは,いいものではありませんね.

*1:Integer#timesで渡される値は0から始まります.リファレンスマニュアルにも書かれていますし,「ruby -e '5.times{|i|puts i}'」を実行しても確認できます.

*2:例えばn=8のとき,真ん中で'*'を8個出力しているのに対して,'*'がある行数は7個です.