From: Louis J Scoras Date: 2006-06-25T22:34:40+09:00 Subject: Re: [QUIZ] pp Pascal (#84) #!/usr/bin/env ruby # # pascal.rb - Pretty-print Pascal's Triangle # # Copyright (C) 2006 Louis J. Scoras # # This program is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; # either version 2 of the License, or (at your option) any # later version. # # This program is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR # PURPOSE. See the GNU General Public License for more # details. # # [http://www.gnu.org/copyleft/gpl.html] # Here's my solution for Ruby Quiz 84. I realize that you can calculate the # last row in the expansion using the binomial theorem, but you'll need to # iterate through each row anyway to print the numbers out, so I just went # with a caching method. # # I was going to implement a persistant disk cache, but alas I find myself # currently stuck on a windows machine, which takes all the fun out of it-- # for me at least :) # # Thanks to Dirk and James for a great quiz. require 'enumerator' class PascalsRow attr_reader :numbers def initialize(numbers = [1], depth = 0) @numbers = numbers @depth = depth end def even? @depth % 2 == 0 end def calculate_next sums = if @numbers.length == 1 [1] else @numbers.enum_cons(2).collect {|j,k| j+k} end sums = sums << sums.last if even? && @depth != 0 self.class.new([1] + sums, @depth + 1) end def canonical half = @numbers.dup half.pop half.pop unless even? @numbers + half.reverse end def to_s canonical.join(' ') end def padded_string(word_size) canonical.collect do |n| digits = n.to_s padding = ((word_size - digits.length) / 2.0).ceil make_padding(padding) + digits + make_padding(word_size - digits.length - padding, 1) end.join end private def make_padding(size, min_length = 0) ' ' * [size, min_length].max end end class PascalsTriangle def initialize @rows = [PascalsRow.new] end def iterate_rows(n, &block) current_row = @rows.first block.call(current_row) if block n.times do |n| current_row = if @rows[n+1] @rows[n+1] else current_row = current_row.calculate_next @rows[n+1] = current_row end block.call(current_row) if block end current_row end def pretty(n) last_line = iterate_rows(n) word_size = last_line.numbers.last.to_s.length width = last_line.padded_string(word_size).length iterate_rows(n) do |row| padded = row.padded_string(word_size) puts (' ' * ((width - padded.length) / 2)) + padded end end end pt = PascalsTriangle.new pt.pretty((ARGV[0] ? ARGV[0].to_i : 10) - 1)