Ruby Greed Koan - Как я могу улучшить свой суп if / then? - PullRequest
14 голосов
/ 20 января 2011

Я пробираюсь через Ruby Koans, чтобы попытаться выучить Ruby, и пока все хорошо.Я добрался до жадного коана, который на момент написания этой статьи составлял 183. У меня есть рабочее решение, но я чувствую, что собрал воедино только кучу логики if / then и что я неиспользование шаблонов Ruby.

В следующем коде, есть ли способы, по которым вы бы указали мне на более полное использование шаблонов Ruby?(Мой код обернут в комментарии «МОЙ КОД [НАЧИНАЕТСЯ | КОНЕЦ] ЗДЕСЬ».

# Greed is a dice game where you roll up to five dice to accumulate
# points.  The following "score" function will be used calculate the
# score of a single roll of the dice.
#
# A greed roll is scored as follows:
#
# * A set of three ones is 1000 points
#
# * A set of three numbers (other than ones) is worth 100 times the
#   number. (e.g. three fives is 500 points).
#
# * A one (that is not part of a set of three) is worth 100 points.
#
# * A five (that is not part of a set of three) is worth 50 points.
#
# * Everything else is worth 0 points.
#
#
# Examples:
#
# score([1,1,1,5,1]) => 1150 points
# score([2,3,4,6,2]) => 0 points
# score([3,4,5,3,3]) => 350 points
# score([1,5,1,2,4]) => 250 points
#
# More scoring examples are given in the tests below:
#
# Your goal is to write the score method.

# MY CODE BEGINS HERE

def score(dice)

  # set up basic vars to handle total points and count of each number
  total = 0
  count = [0, 0, 0, 0, 0, 0]

  # for each die, make sure we've counted how many occurrencess there are
  dice.each do |die|
    count[ die - 1 ] += 1
  end

  # iterate over each, and handle points for singles and triples
  count.each_with_index do |count, index|
    if count == 3
      total = doTriples( index + 1, total )
    elsif count < 3
      total = doSingles( index + 1, count, total )
    elsif count > 3
      total = doTriples( index + 1, total )
      total = doSingles( index + 1, count % 3, total )
    end
  end

  # return the new point total
  total

end

def doTriples( number, total )
  if number == 1
    total += 1000
  else
    total += ( number ) * 100
  end
  total
end

def doSingles( number, count, total )
  if number == 1
    total += ( 100 * count )
  elsif number == 5
    total += ( 50 * count )
  end
  total
end

# MY CODE ENDS HERE

class AboutScoringProject < EdgeCase::Koan
  def test_score_of_an_empty_list_is_zero
    assert_equal 0, score([])
  end

  def test_score_of_a_single_roll_of_5_is_50
    assert_equal 50, score([5])
  end

  def test_score_of_a_single_roll_of_1_is_100
    assert_equal 100, score([1])
  end

  def test_score_of_multiple_1s_and_5s_is_the_sum_of_individual_scores
    assert_equal 300, score([1,5,5,1])
  end

  def test_score_of_single_2s_3s_4s_and_6s_are_zero
    assert_equal 0, score([2,3,4,6])
  end

  def test_score_of_a_triple_1_is_1000
    assert_equal 1000, score([1,1,1])
  end

  def test_score_of_other_triples_is_100x
    assert_equal 200, score([2,2,2])
    assert_equal 300, score([3,3,3])
    assert_equal 400, score([4,4,4])
    assert_equal 500, score([5,5,5])
    assert_equal 600, score([6,6,6])
  end

  def test_score_of_mixed_is_sum
    assert_equal 250, score([2,5,2,2,3])
    assert_equal 550, score([5,5,5,5])
  end

end

Большое спасибо за любую помощь, которую вы можете оказать, когда я пытаюсь разобраться с Ruby.

Ответы [ 37 ]

41 голосов
/ 02 сентября 2011

Вау! Здесь делается много действительно классных подходов. Мне нравится творчество каждого. Однако у меня педагогическая проблема со всеми ответами, представленными здесь. («Педагогика - это изучение… процесса обучения». - Википедия)

Из первых нескольких коанов (обратно в about_asserts.rb) очевидно, что Путь к Просветлению не требует каких-либо предварительных / внешних знаний о Ruby. Также кажется довольно ясным, что Path даже не требует предварительного опыта программирования. Таким образом, с образовательной точки зрения этот коан должен быть ответственным , используя только методы, языковые конструкции и методы программирования, которые преподавались в более ранних коанах. Это значит:

  • нет Enumerable#each_with_index
  • нет Enumerable#count
  • нет Enumerable#sort
  • нет Hash.new(0) указав значение по умолчанию
  • нет Numeric#abs
  • нет Numeric#divmod
  • без рекурсии
  • нет case when
  • и т.д.

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

Кроме того, поскольку шаблон был просто

def score(dice)
  # You need to write this method
end

казалось, подразумевается, что решение не должно определять другие методы или классы. То есть вам следует заменить только строку # You need to write this method.

Вот решение, которое соответствует моим философским требованиям:

def score (dice)
    sum = 0
    (1..6).each do |i|
        idice = dice.select { |d| d == i }
        count = idice.size

        if count >= 3
            sum += (i==1 ? 1000 : i*100)
        end
        sum += (count % 3) * 100   if i == 1
        sum += (count % 3) *  50   if i == 5
    end
    sum
end

Методы / конструкции здесь представлены в следующих файлах koan:

Enumerable#each    about_iteration.rb
Enumerable#select  about_iteration.rb
Array#size         about_arrays.rb
a ? b : c          about_control_statements.rb
%                  about_control_statements.rb

Вопросы по StackOverflow:

22 голосов
/ 30 октября 2011

Студент спросил Джошу: «Как я могу написать алгоритм для подсчета очков для игры в кости?»

Джошу ударил студента палкой и сказал: «Используйте калькулятор».

def score(dice)
  score = [0, 100, 200, 1000, 1100, 1200][dice.count(1)]
  score += [0, 50, 100, 500, 550, 600][dice.count(5)]
  [2,3,4,6].each do |num|
      if dice.count(num) >= 3 then score += num * 100 end
  end
  score
end
6 голосов
/ 02 августа 2011

Я прошел и прошел каждый тест по одному. Не уверен, что это очень "рубиновое" решение, но мне нравится, что очевидно, что делает каждый раздел, и что нет лишних объявлений значений

def score(dice)
  ## score is set to 0 to start off so if no dice, no score
  score = 0
  ## setting the 1000 1,1,1 rule
  score += 1000 if (dice.count(1) / 3) == 1
  ## taking care of the single 5s and 1s here
  score += (dice.count(5) % 3) * 50
  score += (dice.count(1) % 3) * 100
  ## set the other triples here
  [2, 3, 4, 5, 6].each do |num|
    score += num * 100 if (dice.count(num) / 3 ) == 1
  end
  score
end
4 голосов
/ 20 января 2011

выглядит хорошо. Я мог бы написать некоторые вещи немного по-другому, скажем:

def do_triples number, total
  total + (number == 1 ? 1000 : number * 100)
end

Если вы хотите сделать что-то, что могут сделать несколько языков, отличных от Ruby, я полагаю, что следующие могут быть оправданными при DIE и DRY, по вторникам, но я не думаю, что эти максимы Ruby были действительно предназначен для применения к устранению общего подвыражения. В любом случае:

def do_triples number, total
  total +
  if number == 1
    1000
  else
    number * 100
  end
end

def do_triples number, total
  if number == 1
    1000
  else
    number * 100
  end + total
end
4 голосов
/ 19 июня 2012

Вот что я сделал.Выглядит очень похоже на несколько старых ответов.Я хотел бы найти какое-нибудь оригинальное использование inject для этого (mikeonbike's niiiice).

def score(dice)
  total = 0

  # handle triples scores for all but '1'
  (2..6).each do |num|
    total += dice.count(num) / 3 * num * 100
  end

  # non-triple score for '5'
  total += dice.count(5) % 3 * 50

  # all scores for '1'
  total += dice.count(1) / 3 * 1000 + dice.count(1) % 3 * 100

  total
end
3 голосов
/ 04 ноября 2012

Вот ответ, который я получил после примерно четырех итераций и пытаясь использовать в своих интересах конструкции Ruby, которые я изучаю, используя коаны:

def score(dice)
  total = 0
  (1..6).each { |roll| total += apply_bonus(dice, roll)}
  return total
end

def apply_bonus(dice, roll, bonus_count = 3)
  bonus = 0
  bonus = ((roll == 1 ? 1000 : 100) * roll) if (dice.count(roll) >= bonus_count)
  bonus += 50 * (dice.count(5) % bonus_count) if (roll == 5)
  bonus += 100 * (dice.count(1) % bonus_count)  if (roll == 1)
  return bonus
end
3 голосов
/ 06 марта 2011

Вы можете сократить это до меньшего количества строк, но читаемость алгоритма теряется, поэтому я закончил с этим:

def score(dice)
  result = 0;

  (1..6).each do |die|
    multiplier = die == 1 ? 1000 : 100
    number_of_triples = dice.count(die) / 3
    result += die * multiplier * number_of_triples
  end

  result += 100 * (dice.count(1) % 3)

  result += 50 * (dice.count(5) % 3)
end

А если вы используете 1.8.6, вам придется использовать backports или добавить метод подсчета в Array самостоятельно:

class Array
  def count(item)
    self.select { |x| x == item }.size
  end
end
2 голосов
/ 07 октября 2012

Это самое простое и удобочитаемое решение, которое я придумал. Это также учитывает несколько ситуаций, которых нет в тестах, например, бросок шести 5 или шести 1.

def score(dice)
  score = 0
  (1..6).each { |d|
    count = dice.find_all { |a| a == d }
    score = ( d == 1 ? 1000 : 100 ) * d if count.size >= 3
    score += (count.size - 3) * 50 if (count.size >= 4) && d == 5
    score += (count.size - 3) * 100 if (count.size >= 4) && d == 1  
    score += count.size * 50 if (count.size < 3) && d == 5
    score += count.size * 100 if (count.size < 3) && d == 1
  }
  score
end

Я решил использовать метод size вместо метода count, поскольку count не поддерживается всеми версиями Ruby, и коаны не проверяли счет до этого теста.

2 голосов
/ 25 ноября 2012
def score(dice)
  total = 0
  sets = dice.group_by{|num| num }

  sets.each_pair do |num, values|
    number_of_sets, number_of_singles = values.length.divmod(3)
    number_of_sets.times { total += score_set(num) }
    number_of_singles.times { total += score_single(num) }
  end

  total
end

def score_set(num)
  return 1000 if num == 1
  num * 100
end

def score_single(num)
  return 100 if num == 1
  return 50 if num == 5
  0
end
2 голосов
/ 29 июля 2011

Еще один ответ:)

def score(dice)
  score = 0
  for num in 1..6
    occurrences = dice.count {|dice_num| dice_num == num}
    score += 1000 if num == 1 and occurrences >= 3
    score += 100 * (occurrences % 3) if num == 1
    score += 100 * num if num != 1 and occurrences >= 3
    score += 50 * (occurrences % 3) if num == 5
  end
  score
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...