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 ]

0 голосов
/ 15 января 2012

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

Вот мое решение.Я уверен, что это можно улучшить, но посмотрите на форму функции «оценка».Это тот код, который я не против сохранить.

class Array
  def occurrences_of(match)
    self.select{ |number| match == number }.size
  end

  def delete_one(match)
    for i in (0..size)
      if match == self[i]
        self.delete_at(i)
        return
      end
    end
  end
end

def single_die_rule(match, score, dice)
  dice.occurrences_of(match) * score
end

def triple_rule(match, score, dice)
  return 0 if dice.occurrences_of(match) < 3
  3.times { dice.delete_one match }
  score
end

def score(dice)
  triple_rule(1, 1000, dice) +
  triple_rule(2, 200, dice) +
  triple_rule(3, 300, dice) +
  triple_rule(4, 400, dice) +
  triple_rule(5, 500, dice) +
  triple_rule(6, 600, dice) +
  single_die_rule(1, 100, dice) +
  single_die_rule(5, 50, dice)
end
0 голосов
/ 31 января 2012

Мне нужно идти с:

def score(dice)
    # some checks
    raise ArgumentError, "input not array" unless dice.is_a?(Array)
    raise ArgumentError, "invalid array size" unless dice.size <= 5
    raise ArgumentError, "invalid dice result" if dice.any? { |x| x<1 || x>6 }

    # setup (output var, throws as hash)
    out = 0
    freqs = dice.inject(Hash.new(0)) { |m,x| m[x] += 1; m }

    # 3-sets
    1.upto(6) { |i| out += freqs[i]/3 * (i == 1 ? 10 : i) * 100 }

    # one not part of 3-set
    out += (freqs[1] % 3) * 100

    # five not part of 3-set
    out += (freqs[5] % 3) * 50

    out
end

Поскольку большинство представленных решений пока не проходят базовых проверок. И некоторые из них довольно нечитабельны (в моей книге) и не очень идиоматичны.

Конечно, условие с тремя наборами можно сделать более читабельным, разбив его на две части:

    # 3-sets of ones
    out += freqs[1]/3 * 1_000
    # 3-sets of others
    2.upto(6) { |i| out += freqs[i]/3 * i * 100 }

но это IMO в основном о личных предпочтениях.

0 голосов
/ 27 марта 2014

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

def score(dice)
  score = 0
  (1..6).each do |roll|
    count = dice.count(roll)

    # Triples
    score += roll == 1 ? 1000 : roll * 100 if count >= 3

    # And the rest
    score += {1 => 100, 5 => 50}[roll].to_i * (count % 3)
  end
  score
end
0 голосов
/ 10 февраля 2015
def score(dice)
  score = 0
  temp = dice.inject({}) do | result, elem|
    result[elem] = (result[elem]) ? result[elem] + 1 : 1
    result
  end
  temp.each  do |key, value|
    score += (value/3 * 1000) + (value%3)*100 if key == 1
    score += (value/3 * key * 100) + (value%3)*50 if key == 5
    score += (value/3*key*100) if key != 5 && key != 1 && value >= 3
  end
  score
end
0 голосов
/ 10 августа 2011

Я использовал немного другой метод, чем другие, и (естественно) это тот, который я считаю предпочтительным. Это очень СУХОЙ и использует методы ruby ​​довольно широко, чтобы избежать ручных циклов и ветвей в максимально возможной степени. Должно быть относительно очевидно, но, по сути, происходит то, что мы перебираем каждый уникальный бросок костей и используем итеративную эрозию числа повторений этого броска, чтобы добавить соответствующие баллы к совокупному общему счету. *

def score(dice)
  score = 0 # An initial score of 0.

  throw_scores = { 1 => 10, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6 }
    # A hash to store the scores for each dice throw

  dice.uniq.each { |throw| # for each unique dice value present in the "hand"

    throw_count = (dice.select { |item| item == throw }).count
      # use select to store the number of times this throw occurs

    while throw_count > 0 
      # iteratively erode the throw count, accumulating 
      # points as appropriate along the way.

      if throw_count >= 3
        score += throw_scores[throw] * 100
        throw_count -= 3
      elsif throw == 1 || throw == 5
        score += throw_scores[throw] * 10
        throw_count -= 1
      else
        throw_count -= 1
      end
    end
  }
  return score
end
0 голосов
/ 11 августа 2016

Несколько хороших ответов здесь, время для еще одного?

Я использовал подход поиска для минимизации условных операторов - так что только один, если.[И я думаю, что я использовал только то, что уже было введено в коанах.]

def score(dice)

count = [0]*7
score = [0, 100, 0, 0, 0, 50, 0]
bonus = [0, 700, 200, 300, 400, 350, 600]

total = 0

dice.each do |roll|

    total += score[roll]

    count[roll] += 1    
    total += bonus[roll] if count[roll]==3

end 

total

end

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

0 голосов
/ 01 февраля 2012

Исходя из Perl, мой инстинкт заключается в использовании хэша:

def score(dice)
  # You need to write this method
  score = 0
  count = Hash.new(0)

  for die in dice
    count[die] += 1

    is_triple = (count[die] % 3 == 0)
    if die == 1 then
      score += is_triple ? 800 : 100
    elsif die == 5 then
      score += is_triple ? 400 : 50
    elsif is_triple
      score += 100 * die
    end
  end

  return score
end

Преимущество этого в том, что он делает один проход перед dice.Я мог бы использовать Array вместо Hash.

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