HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Look-and-say sequence

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
andsaylooksequence

Problem

Here is my code in Ruby that shows the look-and-say sequence. It shows correct result, but my teacher said, that I could actually make it better. Can you tell me what the flaws are there in my code?

In the input I tell how many sequence elements I want to see.
For example I type

5


and get:

1
11
21
1211
111221


class NumberSequence

  def self.start
    puts 'Enter the number of elements in sequence'
    count = gets.to_i - 1
    number = "1"
    times = 1
    puts number
    count.times do
        result = ""
        c = number[0]
        number = number[1..-1]+" "
        number.chars.each do |char|
       if(c!=char)
          result+= times.to_s+c
          times = 1
          c = char
      else
        times+=1
      end
    end
    number = result
    puts(result)
  end
end
end

NumberSequence.start

Solution

Interface design

You've lumped all of the functionality into one function. At the least, you should have a function that is responsible for sequence-generating, and another for the prompting and printing.

To me, this problem cries out to be treated as a enumerator. With an enumerator, you would automatically benefit from all of the members of the Enumerable mixin, especially #take, which takes the first n elements.

Since this is an infinite sequence, one tricky bit is to ensure that you define a lazy enumerator so that it doesn't try to generate the entire infinite sequence.

Implementation

The implementation would be a lot simpler if you took advantage of Enumerable#chunk.

Suggested solution

module LookAndSaySequence

  # An enumerator that generates the sequence "1", "11", "21", "1211", ...
  # (or with some other initial string of digits)
  def self.strings(init='1')
    Enumerator.new do |y|
      s = init
      loop do
        y << s
        s = s.chars
             .chunk { |digit| digit }
             .map { |digit, list| list.length.to_s + digit }
             .join
      end
    end.lazy
  end
end

print 'Enter the number of elements in sequence: '
LookAndSaySequence.strings.take(gets.to_i).each { |s| puts s }


… and a bonus variant LookAndSaySequence.numbers that produces Fixnums instead of Strings:

module LookAndSaySequence

  # An enumerator that generates the sequence 1, 11, 21, 1211, ...
  # (or with some other initial number)
  def self.numbers(init=1)
    strings(init.to_s).map { |s| s.to_i }
  end
end

Code Snippets

module LookAndSaySequence

  # An enumerator that generates the sequence "1", "11", "21", "1211", ...
  # (or with some other initial string of digits)
  def self.strings(init='1')
    Enumerator.new do |y|
      s = init
      loop do
        y << s
        s = s.chars
             .chunk { |digit| digit }
             .map { |digit, list| list.length.to_s + digit }
             .join
      end
    end.lazy
  end
end

print 'Enter the number of elements in sequence: '
LookAndSaySequence.strings.take(gets.to_i).each { |s| puts s }
module LookAndSaySequence

  # An enumerator that generates the sequence 1, 11, 21, 1211, ...
  # (or with some other initial number)
  def self.numbers(init=1)
    strings(init.to_s).map { |s| s.to_i }
  end
end

Context

StackExchange Code Review Q#110552, answer score: 4

Revisions (0)

No revisions yet.