Ruby и красивый код: Рефаторинг поиска палиндромов

августа 27, 2011  |  Published in Ruby, Основы  |  4 Comments

В уютном бложеке одного из комментаторов нашел один пост-решение задачи, который меня заинтересовал ().

Задача состоит вот в чем:
Необходимо найти числа-палиндромы (которые при чтении слева и справа выглядят одинаково). Автор (к сожалению имени не знаю) предлагает следующее решение:

#Создаем массив чисел
numbers = []
(100).step(999,1) do |first|
  (100).step(999,1) do |second|
    numbers.push first * second
  end
end

#находим палиндром
polinoms = []
numbers.map do |subnumber|
  polinoms << subnumber if subnumber.to_s == subnumber.to_s.reverse
end

#максимальный палиндром
puts polinoms.max

Совет: Оборачивайте код в методы.

А теперь критика.

Мы создаем массив из 899 * 899 = 808201 элемента, на моем Dell D500 перемножение чисел заняло примерно полминуты — слишком много. Да и зачем нам необходимо хранить все эти числа? Нам необходимы только палиндромы! По условию мы должны использовать 3-х значные числа, точнее автор имел ввиду то, что мы перемножаем трехзначные числа для получения чисел для реальной выборки палиндромов, а это 5-ти — 6-ти значные числа. Принимаем эти правила.

Итак, условия таковы: найти палиндромы в пределах всех 5-ти и 6-ти значных чисел.


def palindrome?(num)
  num.to_s == num.to_s.reverse
end

def get_palindromes(start, finish)
  palindromes = []

  (start..finish).each do |num|
    palindromes << num if palindrome?(num)
  end

  return palindromes
end


get_palindromes(100*100, 999*999)

#[10001,10101,10201,...995599,996699,997799]

Результат вываливается практически мгновенно, код несколько длиннее, но читабельнее. Это не все, вы видите, что здесь мы работаем с диапазонами, по ‘этому  можно расширить клас диапазонов — Range:


class Range
  def palindrome?(num)
    num.to_s == num.to_s.reverse
  end

  def get_palindromes
    palindromes = []
    self.each do |num|
      palindromes << num if palindrome?(num)
    end
    return palindromes
  end
end

(100 .. 500).get_palindromes
#=> [101,111,121,131,...474,484,494]

А можно даже так:


module Palindrome
  def palindrome?(num)
    num.to_s == num.to_s.reverse
  end

  def get_palindromes
    palindromes = []
    self.each do |num|
      palindromes << num if palindrome?(num)
    end
    return palindromes
  end

end

class Range
  include Palindrome
end

class Array
  include Palindrome
end

(100..200).get_palindromes
#=> [101,111,121,...171,181,191]

[100,101,200,1245421].get_palindromes
#=> [101,1245421]

Пределов совершенству нет даже в совсем простых задачах.
UPD: А вот и доказательство приведенного выше тезиса, код от brainopia:

class Numeric
  def palindrome?
    to_s == to_s.reverse
  end
end

module Palindrome
  def palindromes
    select &:palindrome?
  end
end
#теперь достаточно подмешать Palindrome в массив или диапазон и можно легко выбрать все числа - палиндромы.
Tags: ,

Responses

  1. brainopia says:

    августа 30, 2011 at 14:58 (#)

    Во-первых, get_palindromes можно упростить до:

    select {|it| palindrome?(it) }
    

    Во-вторых, если уж начали открывать классы, то palindrome? должен быть либо приватным методом, либо быть определен на Numeric и тогда это будет выглядеть следующим образом

    class Numeric
      def palindrome?
        to_s == to_s.reverse
      end
    end
    
    module Palindrome
      def palindromes
        select &:palindrome?
      end
    end
    
  2. admin says:

    августа 31, 2011 at 11:22 (#)

    brainopia, спасибо, совсем забыл о select.

  3. Котт says:

    мая 4, 2012 at 13:15 (#)

    Уж не судите строго,ни чего умнее этого придумать не смог)))

    a = (i..j).to_a
    n=-1
    while n!=j-1
    n+=1
    p = a[n]
    print p, " "  if p.to_s == p.to_s.reverse
    end
    
  4. dimarikpro says:

    марта 27, 2013 at 10:14 (#)

    poli = []
    (10000..999*999).each { |i| poli << i if i.to_s == i.to_s.reverse }

    Зачем все усложнять?

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.