2015-02-27 15 views
0

Помогите мне реорганизовать реализации Luhn algorithm, которая описывается следующим образом:Refactor рубин код алгоритма Лун

формула проверяет ряд против его включить контрольную цифру, которая обычно приложенном к частичному номер счета для создания полный номер . Этот номер счета должен пройти следующее тестирование:

  1. С самой правой цифры, которая является контрольной цифрой, перемещается влево, удваивает значение каждой второй цифры; если произведение этой операции удвоения больше 9 (например, 8 × 2 = 16), затем суммируют цифры продуктов (например, 16: 1 + 6 = 7, 18: 1 + 8 = 9).
  2. Возьмите сумму всех цифр.
  3. Если суммарный модуль 10 равен 0 (если сумма заканчивается на ноль), то число действует в соответствии с формулой Луна; иначе это неверно.

Предположим, пример номер счета "7992739871", который будет иметь контрольную цифру добавил, что делает его формы 7992739871x:

  • Account number 7 9 9 2 7 3 9 8 7 1 x
  • Double every other 7 18 9 4 7 6 9 16 7 2 -
  • Sum of digits 7 9 9 4 7 6 9 7 7 2 =67

Контрольная цифра (x) получается путем вычисления суммы d igits затем вычисляет 9 раз это значение по модулю 10 (в форме уравнения, (67 × 9 mod 10)). В форме алгоритма:

  1. Вычислить сумму цифр (67).
  2. Умножить на 9 (603).
  3. Последняя цифра, 3, является контрольной цифрой. Таким образом, x = 3.

Ниже приводится моя реализация, она работает, но может быть намного лучше, я считаю.

def credit_check(num) 
verify = num.to_s.split('').map(&:to_i) 

half1 = verify.reverse.select.each_with_index { |str, i| i.even? } 
half1 = half1.inject(0) { |r,i| r + i } 

# This implements rule 1 
half2 = verify.reverse.select.each_with_index { |str, i| i.odd? }  
double = half2.map { |n| n * 2 } 
double = double.map { |n| n.to_s.split('') } 
double = double.flatten.map(&:to_i) 
double = double.inject(0) { |r,i| r + i } 

final = double + half1 

puts final % 10 == 0 && (num.to_s.length > 12 && num.to_s.length < 17) ? "VALID" : "INVALID" 
end 

Я вообще-то ранг нуб, очевидно. Но я ценю любую помощь, включая правильный стиль!

+0

Первым шагом является использование редактора, который помогает обеспечить отступ. Это долгий путь к написанию правильного кода. –

+0

Спасибо. Я использовал пико, который не помогает! – sarkon

+0

Есть много хороших редакторов. Двумя основными моделями являются vim и emacs. Они немного кривая обучения, но они работают на нескольких платформах, поэтому, узнав, что один или другой вы можете использовать те же команды и конфигурации на других машинах. Я использую vim для Mac OS, Windows и Linux, все с одинаковыми конфигурациями. Также оба Sublime Text Editor и Textmate хороши; Sublime находится в непрерывном развитии, и Textmate, похоже, устарел. –

ответ

2

Предложений:

  1. Try инкапсулировать код в классе и обеспечивают интуитивный публичный API. Скрыть внутренние детали алгоритма в частных методах.
  2. Препятствуйте правилам небольшим методам в классе, который имеет максимум 5 строк, сломайте это правило экономно. Следуйте за Sandi Metz Rules.
  3. Изучите проблему и найдите доменные имена, относящиеся к проблеме; используйте его, чтобы назвать небольшие методы.
  4. Фокус на читаемость.Помните эту цитату: «Программы должны быть написаны для людей, чтобы читать, и только случайно для машин для выполнения». от Hal Abelson от SICP.
  5. Прочитано Ruby style guide, чтобы улучшить форматирование кода; и да получить лучший редактор.
  6. После этого может показаться, что код более подробный. Но это улучшит читаемость и поможет в обслуживании. Кроме того, если вы склонны следовать ему даже в личных проектах, этот процесс будет выгравирован в вас и скоро станет второй натурой.

С этим в виду, пройти через следующую попытку задачи:

class CreditCard 
    VALID_LENGTH_RANGE = 12..17 

    def initialize(number) 
    @number = number.to_s 
    end 

    def valid? 
    valid_length? && check_sum_match? 
    end 

    private 

    def valid_length? 
    VALID_LENGTH_RANGE.include? @number.length 
    end 

    def check_sum_match? 
    check_sum.end_with? check_digit 
    end 

    def check_sum 
    digits = check_less_number 
      .reverse 
      .each_char 
      .each_with_index 
      .map do |character, index| 
     digit = character.to_i 
     index.even? ? double_and_sum(digit) : digit 
    end 

    digits.reduce(:+).to_s 
    end 

    def check_less_number 
    @number[0..-2] 
    end 

    def check_digit 
    @number[-1] 
    end 

    def double_and_sum(digit) 
    double = digit * 2 
    tens = double/10 
    units = double % 10 

    tens + units 
    end 
end 

Таким образом, вы можете использовать его следующим образом:

CreditCard.new(222222222224).valid? # => true 
CreditCard.new(222222222222).valid? # => false 
0

как об использовании вложенной методе INJECT

half2 = verify.reverse.select.each_with_index { |str, i| i.odd? } 
double = half2.map { |n| n * 2 } 

double = double.inject(0){|x,y| x + y.to_s.split("").inject(0){|sum, n| sum + n.to_i}} 
0

Я бы реализовать этот алгоритм вроде:

def credit_card_valid?(num) 
    digits = String(num).reverse.chars.map(&:to_i) 
    digits.each_with_index.reduce(0) do |acc, (value, index)| 
    acc + if index.even? 
      value 
      else 
      double_value = value * 2 
      if double_value > 9 
       double_value.to_s.split('').map(&:to_i).reduce(&:+) 
      else 
       double_value 
      end 
      end 
    end % 10 == 0 
end 

Ну, этот код работает для тех, xamples из Википедии :)

Вот некоторые советы для вас:

  • избавиться от отпечатков/помещает в стандартный ввод в функции, просто возвращает значение . Для этой функции логическое значение true/false является хорошим.
  • рубин сообщество использует '?' в именах методов, которые возвращают ложные/истинные
  • не забывайте о правильно форматировать код, но может быть, вы, возможно, уже не научились делать это на Stackoverflow (у меня еще нет :)
  • использование 2 пробелы для отступов вашего кода
+0

Это очень просто и понятно. И спасибо за советы. Я не понимаю синтаксис предпоследней строки: 'end% 10 == 0' Я понимаю, что он делает, но не понимаю, как он получает значение, которое он проверяет. Благодаря! – sarkon

+0

Эй, вся конструкция до '% 10' возвращает число, которое является результатом, если это сложное умножение/суммирование из википедии. Поэтому я мог бы назначить его некоторой переменной типа 'sum = digits.each_with_index ... ', а затем использовать ее как' sum% 10 == 0'. Функция возвращает true, если номер кредитной карты действителен и false в противном случае. – tryzniak

Смежные вопросы