2015-09-28 2 views
4

Инструкции для моей программы заключаются в том, чтобы сделать метод, который берет строку и возвращает тире вокруг любых нечетных чисел, но за исключением того, что конечный результат не может начинаться или заканчиваться тире "-". Например, если я ввожу "95601137", он должен вернутьУсловия использования Ruby

"9 - 5 - 6 0 - 1 - 1 - 3 - 7" 

Вот код:

def dashes(number) 
    string = number.to_s 
    i=0 
    final = [] 
    while i<string.length 
    digit = string[i].to_i 
    if ((digit)%2 != 0) && (i==0) && (string.length == 1) && ((string.length - 1) == 0) 
     final.push(string[i]) 
    elsif ((digit)%2 != 0) && (i==0) && (string.length != 1) && ((string.length - 1) > 0) 
     final.push(string[i] + "-") 
    elsif ((digit)%2 != 0) && (i>0) && (i!=(string.length - 1)) && ((string.length != 1)) 
     final.push("-" + string[i] + "-") 
    elsif ((digit)%2 != 0) && (i!=0) && (i==(string.length - 1)) 
     final.push("-" + string[i]) 
    else final.push(string[i]) 
    end 
    i+=1 
    end 
    return final 
end 

puts("Give me any number.") 
answer = gets 
puts dashes(answer) 

Моя программа имеет две проблемы:

  1. Когда я вхожу 9, он возвращает "9-". Как так?
  2. Когда я ввожу строку, которая заканчивается нечетным числом, она помещает тире в конец, чего не ожидается. Я думал, что тоже сделал свои условия if.
+1

Я очень благодарен за все ваши ребята! –

ответ

1

Заменить:

answer = gets 

с:

answer = gets.strip 

Тогда вы получите правильный выход для ввода: 9

Потому что, когда вы даете 9 в качестве входных данных и попадания enter, ваш ввод на самом деле: "9\n" который имеет длину 2. Вот почему это был printi ng 9-. Использование: gets.strip вы только strip вне \n.

Чтобы удалить последний - (если таковые имеются), вы можете использовать chomp:

final_result = final.join('').chomp('-') 

И, чтобы удалить первый - (если таковые имеются), вы можете использовать эту логику:

if final_result[0] == '-' 
    final_result[1..-1] 
    else 
    final_result 
    end 

Ниже приведена полная рабочая версия кода (, хранящем столько кода, как ваш код):

def dashes(number) 
    string = number.to_s 

    i = 0 

    final = [] 

    while i < string.length 

     digit = string[i].to_i 

     if ((digit)%2 != 0) && (i==0) && (string.length == 1) && ((string.length - 1) == 0)    
      final.push(string[i]) 
     elsif ((digit)%2 != 0) && (i==0) && (string.length != 1) && ((string.length - 1) > 0)    
      final.push(string[i] + "-") 
     elsif ((digit)%2 != 0) && (i>0) && (i!=(string.length - 1)) && ((string.length != 1))    
      final.push("-" + string[i] + "-") 
     elsif ((digit)%2 != 0) && (i!=0) && (i==(string.length - 1))     
      final.push("-" + string[i]) 
     else 
      final.push(string[i]) 
     end 

    i += 1 

    end 

    final_result = final.join('').gsub('--', '-').chomp('-') 
    puts "final_result: #{final_result.inspect}" 

    if final_result[0] == '-' 
    final_result[1..-1] 
    else 
    final_result 
    end 
end 

puts("Give me any number.") 

answer = gets.strip 

puts dashes(answer) 

# > Give me any number. 
# > 95601137 
# > "9-5-60-1-1-3-7" 
+1

Ничего себе, спасибо! :) –

+0

Добро пожаловать в коллекцию stackoverflow. Вы всегда можете принять наилучший ответ на свой вопрос, а также вы можете перенести другие ответы, которые вам нравятся, и найти полезные советы. Чтобы узнать, как работает прием ответа, см. Это сообщение: http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work –

2

Я хотел бы предложить другое решение:

def dashes(num) 
    str = num.to_s 
    dashed_arr = str.chars.map.with_index do |digit, index| 
    next digit if digit.to_i.even? 
    if index.zero? # is it first digit? 
     "#{digit} -" 
    elsif index == str.length - 1 # is it last digit? 
     "- #{digit}" 
    else 
     "- #{digit} -" 
    end 
    end 

    dashed_arr.join(' ').gsub('- -', '-') 
end 

puts dashes(95601137) 
puts dashes(909) 
# => "9 - 5 - 6 0 - 1 - 1 - 3 - 7" 
# => "9 - 0 - 9" 

Давайте рассмотрим его шаг за шагом:

  1. str = num.to_s - преобразовать наше число в строковое представление, т.е. "95601137"
  2. str.chars - разделите нашу строку символами. Мы получаем массив символов, например: ["9", "5", "6", "0", "1", "1", "3", "7"], потому что мы хотим их повторить, один за другим.
  3. map принимает один массив и преобразует его в другой массив по коду, указанному в do ... end.

Например, давайте рассмотрим этот код:

[1, 2, 3].map do |num| 
    num * 2 
end 
# => [2, 4, 6] 

Если вам нужно также индекс вашего элемента, вы можете так же использовать .map.with_index (индекс начинается с нуля):

[1, 2, 3].map.with_index do |num, index| 
    num * index # index takes values 0, 1, 2 
end 
# => [0, 2, 6] 

Итак, в блоке в коде выше, у нас есть каждая цифра из нашего номера как digit и ее 0-позиция в виде index.

  1. next digit if digit.to_i.even?. Нам не нужны даже цифры, поэтому мы можем пропустить их. .to_i необходим для преобразования digit в integer, поэтому мы можем запросить его четное или нечетное. next digit просто возвращает текущую цифру и перемещает выполнение до следующей цифры.

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

    if index.zero? # first digit, index is zero-based 
        "#{digit} -" 
    elsif index == str.length - 1 # last digit 
        "- #{digit}" 
    else 
        "- #{digit} -" 
    end 
    
  3. Мы экономим промежуточный результат в dashed_arr переменном, чтобы сделать код читаемым. Сейчас он содержит следующее: ["9 -", "- 5 -", "6", "0", "- 1 -", "- 1 -", "- 3 -", "- 7"]. Как вы можете видеть, мы почти закончили, нам просто нужно связать все элементы массива с строкой.

  4. dashed_arr.join(' '). Объединение элементов массива в строку с одиночным пространством в качестве разделителя. Мы получаем эту строку: 9 - - 5 - 6 0 - 1 - - 1 - - 3 - - 7. Хм, кажется, нам нужно удалить несколько последовательных тире.

  5. Давайте сделаем это с gsub: dashed_arr.join(' ').gsub('- -', '-'). gsub просто ищет все вхождения первой строки и заменяет их второй строкой, которая именно то, что нам нужно: 9 - 5 - 6 0 - 1 - 1 - 3 - 7.

Все готово! Надеюсь, это было так же весело для вас, как и для меня.

+0

Если вы введете «909», выход будет «909», , Я не уверен, но из того, что я понял из проблемы, выход должен быть «9 - 0 - 9» – Wagner

+0

@Wagner Кажется, вы правы, я обновлю свой ответ. –

1

Вот два способа для:

str = "95601137" 

Используйте перечислитель

С помощью этого метода вы будете использовать String#each_char создать перечислитель, к которому вы посылали методы Enumerator#next и Enumerator#peek:

enum = str.each_char 

s = '' 
loop do 
    c = enum.next 
    s << c 
    n = enum.peek 
    s << ((c.to_i.odd? || n.to_i.odd?) ? ' - ' : ' ') 
end 
s #=> "9 - 5 - 6 0 - 1 - 1 - 3 - 7" 

Ключевым моментом здесь является то, что при перечислителе на последнем элементе ('7') peek возникает исключение StopIteration. Исключение обрабатывается Kernel#loop, вырвавшись из цикла.

Использование String#gsub

Мы можем использовать gsub три раза, каждый с регулярным выражением, чтобы:

  • поставить прочерки в правильных местах
  • пространство по желанию вокруг черточек
  • пространство по желанию между четными цифрами

r =/  
    (\A\d) # match beginning of string followed by a digit, save in group 1 
    |  # or 
    (\d\z) # match a digit followed by end of string, save in group 2 
    |  # or 
    (\d) # match a digit, save in capture group 3 
    /x  # extended mode 

str = "95601137" 

str.gsub(r) do |s| 
    if $1 
    $1.to_i.odd? ? "#{$1}-" : $1 
    elsif $2 
    $2.to_i.odd? ? "-#{$2}" : $2 
    elsif $3 
    $3.to_i.odd? ? "-#{$3}-" : $3 
    else 
    raise ArgumentError, "'#{s}' is not a digit" 
    end 
end.gsub(/-+/, ' - ').gsub(/(\d)(\d)/,'\1 \2') 
    #=> "9 - 5 - 6 0 - 1 - 1 - 3 - 7" 
+0

Спасибо, Кэри, это очень интересно! Реализация enumerator вдохновила меня на мысль, что 'each_cons (2)' + 'zip' тоже может работать. Благодаря! –

+0

Алексей, это было бы довольно элегантное решение. Я предлагаю вам отправить ответ. –

0

наивная реализация, то, вероятно, некоторые умные способы сделать это:

def dashes(number) 
    number        # => 95601137 
    .to_s        # => "95601137" 
    .chars       # => ["9", "5", "6", "0", "1", "1", "3", "7"] 
    .map(&:to_i)      # => [9, 5, 6, 0, 1, 1, 3, 7] 
    .map {|i| i.odd? ? "-#{i}-" : i } # => ["-9-", "-5-", 6, 0, "-1-", "-1-", "-3-", "-7-"] 
    .join        # => "-9--5-60-1--1--3--7-" 
    .gsub(/^-|-$/, '')    # => "9--5-60-1--1--3--7" 
    .gsub('--', '-')     # => "9-5-60-1-1-3-7" 
end 
2

еще одна реализация, вдохновленные Cary Swoveland's first implementation:

str = '95601137' 

separators = str.each_char.map(&:to_i).each_cons(2).map do |pair| 
    pair.any?(&:odd?) ? ' - ' : ' ' 
end 

str.chars.zip(separators).join 
# => "9 - 5 - 6 0 - 1 - 1 - 3 - 7" 
+1

Очень хорошо! Небольшое предложение: когда вы разбиваете строку на символы для подачи метода «Enumerable», например 'map', используйте' each_char', который возвращает 'Enumerator', а не' chars', который возвращает (временный) массив , 'zip' - это метод' Array', поэтому вы должны использовать 'chars'. –

+0

В качестве альтернативы, '(str.each_char.map (&: to_i) .each_cons (2) .map do | p, q |" # {p} # {[p, q] .any? (&: Нечетный?)? '-': ''} "; end << str [-1]). join # =>" 9 - 5 - 6 0 - 1 - 1 - 3 - 7 "'. –

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