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 ]

2 голосов
/ 02 января 2013

Это было мое окончательное решение после того, как у меня возникла похожая ошибка if / then / else с моей первой попытки.

def score(dice)
  score = 0
  dice.uniq.each do |roll| 
    score += dice.count(roll) / 3 * (roll == 1 ? 1000 : 100*roll)
    score += dice.count(roll) % 3 * (roll == 1 ? 100 : (roll == 5 ? 50 : 0))
  end
  score
end
1 голос
/ 23 августа 2011

Вот мой ответ. Я не знаю, хорошо это или нет, но, по крайней мере, это выглядит ясно:)

RULEHASH = { 
    1 => [1000, 100],
    2 => [100,0],
    3 => [100,0],
    4 => [100,0],
    5 => [100,50],
    6 => [100,0] 
}

def score(dice)
    score = 0
    RULEHASH.each_pair do |i, rule|
        mod = dice.count(i).divmod(3)
        score += mod[0] * rule[0] * i + mod[1] * rule[1]
    end
    score
end
1 голос
/ 21 июня 2011

Ну,

Вот мое решение:

def score(dice)
    total = 0

    #Iterate through 1-6, and add triples to total if found 
    (1..6).each { |roll| total += (roll == 1 ? 1000 : 100 * roll) if dice.count(roll) > 2 }

    #Handle Excess 1's and 5's
    total += (dice.count(1) % 3) * 100 
    total += (dice.count(5) % 3) * 50
end

Как только я нашел метод "подсчета" для массива, это упражнение было довольно простым.

1 голос
/ 20 апреля 2011

Мои 2 цента.Наличие новых методов для одиночных игр / удвоений кажется окольным способом сделать что-то очень простое.

def score(dice)

  #fill initial throws
  thrown = Hash.new(0)
  dice.each do |die|
    thrown[die]+=1
  end

  #calculate score
  score = 0
  faces.each do |face,amount|
    if amount >= 3
      amount -= 3
      score += (face == 1 ? 1000 : face * 100)
    end
    score += (100 * amount) if (face == 1)
    score += (50 * amount) if (face == 5)
  end

  score
end
1 голос
/ 21 января 2011

Возможно, вы захотите изменить

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

на хеш, например

count = Hash.new(0)
dice.each do |die|
  count[die] += 1
end

или даже

count = {} # Or Hash.new(0)
grouped_by_dots = dice.group_by {|die| die}
1.upto(6) do |dots| # Or grouped_by_dots.each do |dots, dice_with_those_dots|
  dice_with_those_dots = grouped_by_dots.fetch(dots) {[]}
  count_of_that_dots = dice_with_those_dots.length
  count[dots] = count_of_that_dots
end

Таким образом, у вас нетчтобы в вашем коде было засорено index + 1.

Было бы хорошо, если бы в Ruby был встроен метод count_by.

1 голос
/ 13 марта 2012

Мое решение не в стиле ruby.Просто для удовольствия и кратчайшего кода.Мы можем установить правила через хеш p.

def score(dice)
  p = Hash.new([100,0]).merge({1 => [1000,100], 5 => [100,50]})
  dice.uniq.inject(0) { |sum, n| sum + dice.count(n) / 3 * n * p[n][0] + dice.count(n) % 3 * p[n][1] }
end
1 голос
/ 20 января 2011

Я бы сказал, что у вас это уже очень похоже на Ruby. Единственное, что не выглядит для меня очень рубиновым, - это использование имен методов camelCase вместо snake_case, но, конечно, это личное соглашение, и я сам не читал коаны.

Кроме этого, ваш пример не будет значительно улучшен при использовании case / when или любого другого решения по этому вопросу. Стремитесь к чему-то меньшему, чем к 3 другим операциям, если к чему-то большему, и вы, вероятно, захотите найти лучшее решение.

1 голос
/ 16 июля 2012

Мой был похож на пару других, размещенных здесь.

score = 0
[1,2,3,4,5,6].each {|d| 
  rolls = dice.count(d)
  score = (d==1 ? 1000 : 100)*d if rolls >= 3
  score += 100*(rolls % 3) if d == 1 
  score += 50*(rolls % 3) if d == 5 
}
score
1 голос
/ 20 января 2011

Вы могли бы сократить [0, 0, 0, 0, 0, 0] до [0] * 6, но, помимо упоминания camelCase @injekt, оно выглядит для меня нормально.Я был бы очень рад увидеть это в обзоре кода.

Также я полагаю, что вашим doTriples и doSingles действительно не нужны их временные переменные.

def doTriples( number, total )
  if number == 1
    total + 1000
  else
    total + ( number ) * 100 # be careful with precedence here
  end
end
1 голос
/ 25 ноября 2012

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

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]

def score(dice)
  counts = dice.group_by(&:to_i).map { |i, j| [i-1, j.length] }
  counts.inject(0) do |score, (i, count)|
    sets, singles = count.divmod 3

    score + sets * SCORES[i][0] + singles * SCORES[i][1]
  end
end

Вот мой обязательный однострочный текст (и, возможно, версия FP):

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]

def score(dice)
  dice.group_by(&:to_i).inject(0) {|s,(i,j)| s + j.size / 3 * SCORES[i-1][0] + j.size % 3 * SCORES[i-1][1]}
end

Я также прошел несколько странных маршрутов:

SCORES = [[1000, 100], [200, 0], [300, 0], [400, 0], [500, 50], [600, 0]]
def score(dice)
  dice.group_by(&:to_i).inject(0) do |s, (i,j)| 
    s + j.size.divmod(3).zip(SCORES[i-1]).map {|a,b| a*b }.reduce(:+)
  end
end

Все программисты должны крутиться с небольшими проблемами, подобными этим ... Это похоже на выполнение утренних растяжек:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...