[QUIZ] pp Pascal (#84)

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

···

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Dirk Meijer

I recently showed a friend what an amazing language Ruby was, by quickly
programming up a script to calculate Fibonacci's Sequence, and his first
response was: "Can you do Pascal's Triangle?" So I did, which proved harder
than expected.

For those not familiar with Pascal's Triangle, it is very similar to Fibonacci's
Sequence. It's a pyramid of numbers. The outside of the pyramid is all ones, the
other numbers are the sum of the two numbers above, like this:

      1
     1 1
    1 2 1
   1 3 3 1
  1 4 6 4 1

The input and output should be as follows:

  $ ruby pp_pascal.rb 10
                              1
                           1 1
                        1 2 1
                     1 3 3 1
                  1 4 6 4 1
               1 5 10 10 5 1
            1 6 15 20 15 6 1
         1 7 21 35 35 21 7 1
      1 8 28 56 70 56 28 8 1
   1 9 36 84 126 126 84 36 9 1

A number should be given as command-line argument. This is the amount of rows
the triangle has. For the output, there should be spacing between the numbers
based on the size of the numbers with the most digits, so it will look like a
proper triangle.

Good luck!

[Editor's Note: If you are working through Chris Pine's Learn to Program, you
can do this problem using only things you learned in the first eight chapters.
Since he doesn't teach how to grab the row count in those pages though, just add
this as the first line of your program: `rows = ARGV[0].to_i]`. After that,
the rows variable will hold the number of rows to print. --JEG2]

sender: "Ruby Quiz" date: "Fri, Jun 23, 2006 at 10:31:52PM +0900" <<<EOQ

[..]
The input and output should be as follows:

  $ ruby pp_pascal.rb 10
                              1
                           1 1
                        1 2 1
                     1 3 3 1
                  1 4 6 4 1
               1 5 10 10 5 1
            1 6 15 20 15 6 1
         1 7 21 35 35 21 7 1
      1 8 28 56 70 56 28 8 1
   1 9 36 84 126 126 84 36 9 1

A number should be given as command-line argument. This is the amount of rows
the triangle has. For the output, there should be spacing between the numbers
based on the size of the numbers with the most digits, so it will look like a
proper triangle.

question:

    Does this triangle:
                               1
                            1 1
                         1 2 1
                      1 3 3 1
                   1 4 6 4 1
               1 5 10 10 5 1
           1 6 15 20 15 6 1
        1 7 21 35 35 21 7 1
    1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
    
conform with the 'proper triangle' definition?

Thanks,
Alex

For another display example, up to row 15:

(Obviously this won't display well in 80 columns, you'll want to copy
it out to a wide fixed width scratchpad to see it best. But it does
make pretty patterns when it wraps :slight_smile:

$ ruby pascal.rb 15
                                                                          1
                                                                     1 1
                                                                1
   2 1
                                                           1 3
        3 1
                                                      1 4
   6 4 1
                                                 1 5 10
       10 5 1
                                            1 6 15
  20 15 6 1
                                       1 7 21 35
       35 21 7 1
                                  1 8 28 56
  70 56 28 8 1
                             1 9 36 84 126
      126 84 36 9 1
                        1 10 45 120 210
252 210 120 45 10 1
                   1 11 55 165 330 462
      462 330 165 55 11 1
              1 12 66 220 495 792
924 792 495 220 66 12 1
         1 13 78 286 715 1287 1716
     1716 1287 715 286 78 13 1
    1 14 91 364 1001 2002 3003
3432 3003 2002 1001 364 91 14
  1

Jacob Fugal

Did you know that you need only 36 bytes of Ruby code to build
the next row, based on the previous row? Really!

And that the 1000-rows-triangle eats 599.701.000 bytes of disk
space and approximately 82.116.176.508 clock cycles?...

gegroet,
Erik V. - http://www.erikveen.dds.nl/

How about something like: http://rephorm.com/files/dump/pascal28-2.txt
(Easier to view in a web browser than email client / terminal)

I error to the left when aligning on the left side of the triangle, and
to the right on the right side.

Hello,

I had fun doing this, but my output - and thus way of thinking - is a
bit different. But I do like my version more. What about you?

kind regards
Robert

Original:
$ ruby pp_pascal.rb 10
                            1
                         1 1
                      1 2 1
                   1 3 3 1
                1 4 6 4 1
             1 5 10 10 5 1
          1 6 15 20 15 6 1
       1 7 21 35 35 21 7 1
    1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1

Mine:
$ .\pascal_triangle.rb 10
                           1
                        1 1
                     1 2 1
                  1 3 3 1
               1 4 6 4 1
            1 5 10 10 5 1
         1 6 15 20 15 6 1
      1 7 21 35 35 21 7 1
   1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1

#! /usr/bin/env ruby

num_lines = ARGV[0].to_i - 1
tri = []
(0..num_lines).each do |n|
  line = [ 1 ]
  (1..n/2).each{|e| line[e] = tri[n-1][e-1] + tri[n-1][e] }
  tri[n] = line + line[0..(n%2)-2].reverse
end

width = Math.log10(tri[num_lines][num_lines/2]).ceil + 1

(0..num_lines).each do |n|
  print ' ' * ((num_lines - n) * width / 2 ) # leading space
  tri[n].each{|val| printf("%#{width}d", val) }
  print "\n"
end

Hi again,

I think my solution ist not very fast, though I don't use recursion to
build the triangle.

How long does your script take for a 666 levels triangle?

$ measure-command { .\pascal_triangle.rb 666 } | select minutes,
seconds, milliseconds | fl

Minutes : 2
Seconds : 41
Milliseconds : 15

# Pascal's Triangle (Ruby Quiz #84)
# by Mustafa Yilmaz

···

#
# This is the second Ruby program I've ever written and it's not optimized (and probably not
# leveraging the power of Ruby), so don't expect to much :wink: The code should be self-explanatory,
# if you have any questions though don't hesitate to ask me.
#
# My approach is to create a PDF file using the PDF::Writer libraries in order to get the
# centering right. Furthermore, I'm computing the binomial coefficent as discussed before to
# approximate the width of the largest number in the triangle.
#
# One could improve the program by dynamically adjusting the used text size to scale the text
# depending on the size of the triangle to make use of the whole page size.
#
# You can download an example pdf file from http://www.mustafayilmaz.net/pascal.pdf
#

begin
  require 'pdf/writer'
rescue LoadError => le
  if le.message =~ %r{pdf/writer$}
    $LOAD_PATH.unshift("../lib")
    require 'pdf/writer'
  else
    raise
  end
end

class Integer
  def factorial
    self <= 1 ? 1 : self * (self-1).factorial
  end
end

class Binomial_Coefficient
  def self.compute(n, r)
    n.factorial / (r.factorial * (n-r).factorial)
  end
end

class Pascal
  def self.create_pdf(lines, font_size, filename)
    max_width = Binomial_Coefficient.compute(lines, lines / 2).to_s.length + 2
    pdf = PDF::Writer.new(:paper => "A4", :orientation => :landscape)
    pdf.select_font "Courier"
    pdf.text "Pascal's Triangle (Ruby Quiz #84)\n\n\n", :font_size => 10, :justification => :center
    previous_result = Array.[](0, 1, 0)
    s = "1".center(max_width)
    while lines > 0 do
      pdf.text "#{s}\n\n", :font_size => font_size, :justification => :center
      current_result = Array.new previous_result[0..-2].each_index do |i|
        current_result << previous_result[i] + previous_result[i+1]
      end s = String.new
      current_result.each_index do |i|
        s << current_result[i].to_s.center(max_width)
      end
      current_result = Array.[](0).concat(current_result) << 0
      previous_result = current_result
      lines -= 1
    end
    pdf.save_as(filename)
  end
end

Pascal.create_pdf(20, 8, "pascal.pdf")

Hi everybody,

here's my solution, next to few examples.
The result might look pretty poor displayed on an email client :-(, but the
script does print a perfectly equilateral triangle.

Thanks for the interesting quiz!

$ ./pascal.rb 12
                          1
                        1 1
                      1 2 1
                    1 3 3 1
                  1 4 6 4 1
                1 5 10 10 5 1
              1 6 15 20 15 6 1
            1 7 21 35 35 21 7 1
          1 8 28 56 70 56 28 8 1
        1 9 36 84 126 126 84 36 9 1
      1 10 45 120 210 252 210 120 45 10 1
    1 11 55 165 330 462 462 330 165 55 11 1
  1 12 66 220 495 792 924 792 495 220 66 12 1

$ ./pascal.rb 12 -1
  1
  1 1
  1 2 1
  1 3 3 1
  1 4 6 4 1
  1 5 10 10 5 1
  1 6 15 20 15 6 1
  1 7 21 35 35 21 7 1
  1 8 28 56 70 56 28 8 1
  1 9 36 84 126 126 84 36 9 1
  1 10 45 120 210 252 210 120 45 10 1
  1 11 55 165 330 462 462 330 165 55 11 1

$ ./pascal.rb 12 1
                                              1
                                          1 1
                                      1 2 1
                                  1 3 3 1
                              1 4 6 4 1
                          1 5 10 10 5 1
                      1 6 15 20 15 6 1
                  1 7 21 35 35 21 7 1
              1 8 28 56 70 56 28 8 1
          1 9 36 84 126 126 84 36 9 1
      1 10 45 120 210 252 210 120 45 10 1
  1 11 55 165 330 462 462 330 165 55 11 1

$ ./pascal.rb 12 0.7

                                   1
                                1 1
                             1 2 1
                          1 3 3 1
                       1 4 6 4 1
                    1 5 10 10 5 1
                 1 6 15 20 15 6 1
              1 7 21 35 35 21 7 1
           1 8 28 56 70 56 28 8 1
        1 9 36 84 126 126 84 36 9 1
     1 10 45 120 210 252 210 120 45 10 1
  1 11 55 165 330 462 462 330 165 55 11 1

By tweaking a bit the script you can easily obtain a Sierpinski triangle, as
mentioned in a previous post:

./sierpinski.rb 32

···

*

                             * * *
                              * *
                           * * *

                         * * * * * * *
                          * * * * * *
                       * * * * * * *
                            * * * *
                     * * * * * * * * *
                      * * * * * *
                   * * * * * * *

                 * * * * * * * * * * * * * * *
                  * * * * * * * * * * * * * *
               * * * * * * * * * * * * * * *
                    * * * * * * * * * * * *
             * * * * * * * * * * * * * * * * *
              * * * * * * * * * * * * * *
           * * * * * * * * * * * * * * *
                        * * * * * * * *
         * * * * * * * * * * * * * * * * * * * * *
          * * * * * * * * * * * * * * * * * *
       * * * * * * * * * * * * * * * * * * *
            * * * * * * * * * * * *
     * * * * * * * * * * * * * * * * * * * * *
      * * * * * * * * * * * * * *
   * * * * * * * * * * * * * * *

#! /usr/bin/ruby
#
############### Pascal Triangle by Eric DUMINIL ###############
#
# 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.
#
# How-to use it?
# pascal.rb height excentricity
# with:
# height, pretty self-explanatory
# excentricity, float between -1 and 1.
#
# height set to 5 and excentricity set to -1:
#
# 1
# 1 1
# 1 2 1
# 1 3 3 1
# 1 4 6 4 1
#
# height set to 5 and excentricity set to 1 :
#
# 1
# 1 1
# 1 2 1
# 1 3 3 1
# 1 4 6 4 1
#
#
# By default, excentricity is set to 0:
#
# 1
# 1 1
# 1 2 1
# 1 3 3 1
# 1 4 6 4 1
#

#Just to make sure we can calculate C(n,n/2)
#without having to build the whole tree
class Fixnum
    def fact
        return 1 if self<2
        self*(self-1).fact
    end
    
    def cnp(p)
        self.fact/(p.fact*(self-p).fact)
    end
end

class PascalTriangle
    def initialize (height=15,excentricity=0)
        @height=height
        @excentricity=excentricity
        #maxLength should be odd, so that the alignment is preserved
        @maxLength=(height-1).cnp((height-1)/2).to_s.length|1
        createAndShow
    end
    
    attr_reader :height, :maxLength, :excentricity
    
    def createAndShow
        previous=[1]
        current=Array.new
        height.times{|i|
            current[0]=current[i]=1
            #Taking care of the symetry
            1.upto(i/2){|j|
                current[j]=current[i-j]=previous[j]+previous[j-1]
            }
            puts " "*((maxLength+1)*(excentricity+1)/2)*(height-i-1)+
                 current.map{|number|
                    number.to_s.rjust(maxLength)
                 }.join(" ")
            #No need to remember the whole triangle,
      #the previous row will be enough
            previous.replace(current)
        }
    end
end

PascalTriangle.new(ARGV[0].to_i,ARGV[1].to_f)

###############################################################################

Bye,

Eric

Le Vendredi 23 Juin 2006 15:31, Ruby Quiz a écrit :

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz
until 48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone on Ruby Talk follow the discussion. Please reply to the original
quiz message, if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
=-=-=

by Dirk Meijer

I recently showed a friend what an amazing language Ruby was, by quickly
programming up a script to calculate Fibonacci's Sequence, and his first
response was: "Can you do Pascal's Triangle?" So I did, which proved
harder than expected.

For those not familiar with Pascal's Triangle, it is very similar to
Fibonacci's Sequence. It's a pyramid of numbers. The outside of the pyramid
is all ones, the other numbers are the sum of the two numbers above, like
this:

      1
     1 1
    1 2 1
   1 3 3 1
  1 4 6 4 1

The input and output should be as follows:

  $ ruby pp_pascal.rb 10
                              1
                           1 1
                        1 2 1
                     1 3 3 1
                  1 4 6 4 1
               1 5 10 10 5 1
            1 6 15 20 15 6 1
         1 7 21 35 35 21 7 1
      1 8 28 56 70 56 28 8 1
   1 9 36 84 126 126 84 36 9 1

A number should be given as command-line argument. This is the amount of
rows the triangle has. For the output, there should be spacing between the
numbers based on the size of the numbers with the most digits, so it will
look like a proper triangle.

Good luck!

[Editor's Note: If you are working through Chris Pine's Learn to Program,
you can do this problem using only things you learned in the first eight
chapters. Since he doesn't teach how to grab the row count in those pages
though, just add this as the first line of your program: `rows =
ARGV[0].to_i]`. After that, the rows variable will hold the number of rows
to print. --JEG2]

So much for the pp part, but this does the Pascal just fine:

···

----------------------
require "rational"

# I derived the nCk formula myself, didn't wanna cheat.
class Fixnum
  def choose(k); (1..k-1).inject(1){ |s,e| s *= Rational(self,e) - 1 }; end
end

# not to disappoint JEG2, I decided to maintain my record of
# "never completing a ruby quiz", so take the formatting with a grain of salt.
1.upto(ARGV[0].to_i) { |n| puts((1..n).map {|k| n.choose k }.join("|")) }
----------------------

For me the fun part was learning through inspection that the values of
the triangle were simply nCk values, and then deriving the the nCk
formula from the triangle. The above is a bit of a messy version of
that formula, because I did derive it myself, and my math is slow this
summer :wink:

Output (no effort here) :
[sandal@harmonix rubyquiz]$ ruby pascal.rb 15
1
1|1
1|2|1
1|3|3|1
1|4|6|4|1
1|5|10|10|5|1
1|6|15|20|15|6|1
1|7|21|35|35|21|7|1
1|8|28|56|70|56|28|8|1
1|9|36|84|126|126|84|36|9|1
1|10|45|120|210|252|210|120|45|10|1
1|11|55|165|330|462|462|330|165|55|11|1
1|12|66|220|495|792|924|792|495|220|66|12|1
1|13|78|286|715|1287|1716|1716|1287|715|286|78|13|1
1|14|91|364|1001|2002|3003|3432|3003|2002|1001|364|91|14|1

My own solution, used in the quiz examples.

James Edward Gray II

pp_pascal.rb (588 Bytes)

···

On Jun 23, 2006, at 8:31 AM, Ruby Quiz wrote:

by Dirk Meijer

I recently showed a friend what an amazing language Ruby was, by quickly
programming up a script to calculate Fibonacci's Sequence, and his first
response was: "Can you do Pascal's Triangle?" So I did, which proved harder
than expected.

Having followed the discussion of this quiz, I suspect this is going to look a bit bloated compared to some other solutions, as I took the simple approach of building up the entire triangle in memory, then printing it out afterwards. Anyway, here it is:

#!/usr/bin/env ruby
size = ARGV[0].to_i
rows = Array.new(size)
# calculate the numbers
rows[0] = [1]
(1..size - 1).each do |n|
   rows[n] = Array.new(n)
   m = 0
   rows[n - 1].inject 0 do |prev, current|
     rows[n][m] = prev + current
     m += 1
     current
   end
   rows[n] << 1
end
# longest number will be in the middle of the bottom row
max_length = rows[size - 1][size/2 - 1].to_s.length
# pad, centre and output
padded = rows.collect do |row|
   row.inject "" do |line, element|
     line + element.to_s.center(max_length + 2)
   end
end
width = padded[size - 1].length
padded.each {|row| puts row.center(width)}

Output with 13 rows (the most that'll fit in 80 characters):

                                 1
                              1 1
                            1 2 1
                         1 3 3 1
                       1 4 6 4 1
                    1 5 10 10 5 1
                  1 6 15 20 15 6 1
               1 7 21 35 35 21 7 1
             1 8 28 56 70 56 28 8 1
          1 9 36 84 126 126 84 36 9 1
        1 10 45 120 210 252 210 120 45 10 1
     1 11 55 165 330 462 462 330 165 55 11 1
   1 12 66 220 495 792 924 792 495 220 66 12 1

That looks wrong in my mail program, but it does all line up nicely in a monospaced font!

Kerry

My first solution, a straightforward implementation that was
originally recursive, turned iterative:

def pascal n
   rows = []

   # generate data
   (0...n).each do |i|
      rows << if i.zero?
         [1]
      else
         rows[i-1].inject([0]) do |m, o|
            m[0...-1] << (m[-1] + o) << o
         end
      end
   end

   # calc field width
   width = rows[-1].max.to_s.length

   # space out each row
   rows.collect! do |row|
      row.collect { |x| x.to_s.center(2 * width) }.join
   end

   # display triangle
   rows.each { |row| puts row.center(rows[-1].length) }
end

My second solution, after reading some of the discussion, was an
attempt to not generate the whole tree in memory, but only one row at
a time:

class Integer
   def fact
      zero? ? 1 : (1..self).inject { |m, o| m * o }
   end

   def binom(k)
      self.fact / (k.fact * (self - k).fact)
   end
end

def pascal n
   # calc field width
   width = (n - 1).binom(n / 2).to_s.length

   # keep only one row in memory
   row = [1]

   1.upto(n) do |i|
      # print row
      space = ' ' * width * (n-i)
      puts space + row.collect { |x| x.to_s.center(2*width) }.join

      # generate next row
      row = row.inject([0]) { |m, o| m[0...-1] << (m[-1] + o) << o }
   end
end

Either solution started with:

pascal (ARGV[0] || 10).to_i

I could have done some error checking (i.e. bad input) or cached some
values for speed (Integer.fact and Integer.binom, particularly), but I
got lazy...

Hello,

This is the first Ruby Quiz I have tried and I'm glad to have done
it :). Attached is my solution to the quiz.

Cheers.
- --
BASIC is to computer programming as QWERTY is to typing.
                -- Seymour Papert

84.rb (3.07 KB)

Here's an explanation about how to calculate the next row,
based on the previous row. You only need the last line.

row ===> [1, 3, 3, 1]
[0]+row ===> [0, 1, 3, 3, 1]
row+[0] ===> [1, 3, 3, 1, 0]
([0]+row).zip(row+[0]) ===> [[0, 1], [1, 3], [3, 3],
[3, 1], [1, 0]]
([0]+row).zip(row+[0]).map{|a,b|a+b} ===> [1, 4, 6, 4, 1]

You can replace the last line by the next line, but this one
modifies the previous row, which might be tricky. Though it
does save you one extra byte ;]

([0]+row).zip(row<<0).map{|a,b|a+b} ===> [1, 4, 6, 4, 1]

You could build and store the complete triangle with this:

t=[r=[1]]+(2..n).map{r=([0]+r).zip(r+[0]).map{|a,b|a+b}}

(But that's not what we are going to do in this script....)

I added pretty printing as well. First of all, the width of
each cell (excluding the separator) shouldn't be even, in order
to be able to align properly.

Secondly, if we can't center the contents within a cell, we
error to the right on the left hand side of the triangle and to
the left at the right hand side.

Thirdly, because the last line could be stripped by half a cell
width at both the left and the right hand side, we simply strip
every line by half a cell width on both sides.

You can find the source in the attachment.

gegroet,
Erik V. - http://www.erikveen.dds.nl/

pascaltriangle.rb (1.55 KB)

Hi!

I recently showed a friend what an amazing language Ruby was, by
quickly programming up a script to calculate Fibonacci's Sequence,
and his first response was: "Can you do Pascal's Triangle?" So I
did, which proved harder than expected.

Besides the necessary comment that the triangle presented as "Pascal's
Triangle" is already present in the famous Chinese mathematician Zhu
Shijie's "Precious Mirror of the Four Elements" that dates back to
1303 here's my solution:

exit 1 if ARGV.length != 1 || ARGV[0] =~ /[^\d]/
rows = Array.new
current = [1]
rows.push current.dup
(ARGV[0].to_i + 1).times do |row|
  (current.length - 2).times { |i| current[i] += current[i + 1] }
  current[0...0], current[-1] = 1, 1
  rows.push current.dup
end
fieldsize = rows.last.map { |n| n.to_s.length }.max
rows.each_with_index do |row, n|
  print ' ' * (fieldsize + 1) * (rows.length - n - 1)
  puts row.map { |elem| elem.to_s.rjust(fieldsize) + ' ' * (fieldsize + 2) }.join
end

Concerning performance:

time ruby pascal.rb 666 > /dev/null

real 0m7.683s
user 0m7.608s
sys 0m0.044s

I'm using the 32 bit edition of Fedora Core 5 on an Athlon 3700+
running at 2,2 GHz featured with 1 GB RAM.

Josef 'Jupp' Schugt

···

At Fri, 23 Jun 2006 22:31:52 +0900, Ruby Quiz wrote:

I recently showed a friend what an amazing language Ruby was, by quickly
programming up a script to calculate Fibonacci's Sequence, and his first
response was: "Can you do Pascal's Triangle?" So I did, which proved harder
than expected.

Two solutions from me (you may choose if like at least one :))

The first uses hashes to store the values of a line. makes it
pretty obvious what is going on (at least for the author *g*)

···

-------------------------------------------------------------
require 'enumerator'

pascal = [{0, 1}] + (1...ARGV[0].to_i).map{Hash.new(0)}

pascal.each_cons(2) do |last, this|
  last.each{|p, v| this[p - 1] += v; this[p + 1] += v}
end

size = pascal.last.fetch(0, pascal.last[1]).to_s.size + 1

pascal.each do |row|
  line = row.sort.map{|p, v| v.to_s.center size}.join
  puts line.center(size * pascal.last.size).rstrip
end
-------------------------------------------------------------

And the other one with a completely different approach:
-------------------------------------------------------------
fac = lambda{|n| n < 2 ? 1 : (1..n).inject{|f, i| f * i}}
tri = lambda{|n, r| fac[n] / (fac[r] * fac[n-r])}
size = lambda{|r| tri[r-1, r / 2].to_s.size + 1}
line = lambda{|y, r| (0..y).map{|x| tri[y,x].to_s.center size[r]}}
lines = lambda{|r| (0...r).map{|y| line[y, r]}}
pascal = lambda{|r| lines[r].map{|l| l.join.center(size[r] * r).rstrip}}

puts pascal[(ARGV[0] || 15).to_i]
-------------------------------------------------------------

which is more like a case study of functional programming in
ruby (comments welcome, that's not my primary domain)

cheers

Simon

Hi,

here is my solution and a unit test. The triangle is calculated and stored as an array of
arrays first. Then it's converted to a string.

I couldn't resist to add a pair iterator to class Array...

Regards,
Boris

# pascal.rb
class Array
   # iterate through pairs of consecutive elements
   def each_pair
     (0..size-2).each do |i|
       yield(self[i], self[i+1])
     end
   end
end

class Pascal
   def initialize(n)
     @triangle = [[1]]
     2.upto(n) do
       @triangle << calc_row(lastrow)
     end
   end

   def lastrow; @triangle[-1]; end

   # calculate row given the previous row
   def calc_row(previous)
     thisrow = [1]
     previous.each_pair do |x, y|
       thisrow << x + y
     end
     thisrow << 1
   end

   def to_s
     cellwidth = lastrow.max.to_s.size
     indentation = lastrow.size - 1
     emptycell = ' ' * cellwidth
     s = ''
     @triangle.each do |row|
       s << emptycell * indentation
       s << row.map{|cell| cell.to_s.center(cellwidth)}.join(emptycell)
       s << "\n"
       indentation -= 1
     end
     s
   end
end

if $0 == __FILE__
   puts Pascal.new(ARGV[0].to_i).to_s
end

# pascal_test.rb
require 'pascal'
require 'test/unit'

class PascalTest < Test::Unit::TestCase
   def test_1
     expected = "1\n"
     assert_equal expected, Pascal.new(1).to_s
   end

   def test_2
     assert_equal <<EXPECTED, Pascal.new(2).to_s
1
1 1
EXPECTED
   end

   def test_3
     assert_equal <<EXPECTED, Pascal.new(3).to_s
   1
1 1
1 2 1
EXPECTED
   end

   def test_10
     assert_equal <<EXPECTED, Pascal.new(10).to_s
                             1
                          1 1
                       1 2 1
                    1 3 3 1
                 1 4 6 4 1
              1 5 10 10 5 1
           1 6 15 20 15 6 1
        1 7 21 35 35 21 7 1
     1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
EXPECTED
   end
end

Here's my solution.
It's not brief but works anyway.

···

-------------------------------------------

class PascalTriangle

  include Enumerable

  attr_reader :rows

  def initialize row_count
    raise ArgumentException unless row_count.class == Fixnum
    raise "row_count must be greater than 0." unless row_count > 0
    @row_count = row_count
    generate_rows
  end

  def to_s
    max_width = @rows.flatten.max.to_s.size
    max_count = @rows.map{ |i| i.size }.max
    line_width = max_count * max_width * 2 - max_width
    separator = " " * max_width
    self.map do |row|
      row.map { |num| num.to_s.center(max_width)
}.join(separator).center(line_width)
    end.join("\n")
  end

  def each
    @rows.each { |row| yield row }
  end

private

  def generate_rows
    row = []
    @rows = (0...@row_count).to_a.inject([]) do |m, i|
      m << row = generate_row(row)
    end
    @rows.freeze
    @rows.each { |r| r.freeze }
  end

  def generate_row row
    result = [1]
    row.each_with_index do |el, idx|
      result << (idx == row.size - 1 ? 1 : el + row[idx + 1])
    end
    result
  end

end

if ARGV[0].nil? or ARGV[0] !~ /^\d+$/
  puts "Example: ruby pascal.rb 10"
  exit
end

row_count = ARGV[0].to_i
puts PascalTriangle.new(row_count).to_s

-----------------------

Sam Kong

--
Posted via http://www.ruby-forum.com/.