Хобрук: Ваш путь к мастерству в программировании

Как поочередно увеличивать/уменьшать регистр переданной строки по индексу каждого символа?

Я наткнулся на странную вещь, работающую над проблемой Codewars. Вот инструкции:

Напишите функцию toWeirdCase (weirdCase в Ruby), которая принимает строку и возвращает ту же строку со всеми четными индексными символами в каждом слове в верхнем регистре и всеми нечетными индексными символами в каждом слове в нижнем регистре. Только что объясненная индексация основана на нуле, поэтому нулевой индекс четный, поэтому этот символ должен быть в верхнем регистре.

Передаваемая строка будет состоять только из букв алфавита и пробелов (' '). Пробелы будут присутствовать только в том случае, если слов несколько. Слова будут разделены одним пробелом (' '). Примеры:

weirdcase( "String" )#=> returns "StRiNg"
weirdcase( "Weird string case" );#=> returns "WeIrD StRiNg CaSe"

Вот мой код:

def weirdcase string

  @case = []

# Ternary:
  string.each_char { |c|
#    string.index(c).even? ? @case.push(c.upcase) : @case.push(c.downcase)
    c =~ /[[:alpha:]]/ ? (string.index(c).even? ? (@case.push(c.upcase)) : (@case.push(c.downcase))) : (string.index(c) + 1)
    }

# If/Then:
#  string.each_char { |c|
    #if string.index(c).even? && c != " " 
#    if c =~ /[[:alpha:]]/ && string.index(c).even?
#    then @case.push(c.upcase)
#    else @case.push(c.downcase)
#    end  }

  @case.join

end
p "TEST"
p weirdcase "this is a test"
p weirdcase "thisisatest"
p weirdcase " th is  is  a  t es t"

Результаты, достижения:

"TEST"
"ThIsIsATesT"
"ThIsIsATEsT"
"tHIsIsAtest"
weirdcase
should return the correct value for multiple words
Expected: "ThIs Is A TeSt", instead got: "ThIsIsATesT"
0 Passed
1 Failed
0 Errors

Process took 171ms to complete

А вот и тесты:

describe 'weirdcase' do
  #it 'should return the correct value for a single word' do
   #  Test.assert_equals(weirdcase('This'), 'ThIs');
  #  Test.assert_equals(weirdcase('is'), 'Is');
  #end
  it 'should return the correct value for multiple words' do
    Test.assert_equals(weirdcase('This is a test'), 'ThIs Is A TeSt');
  end
end

Я пытался написать этот код несколькими разными способами, но все время получаю один и тот же результат: вместо результата «Это тест» я получаю «ThIsIsATesT» или какой-то другой вариант, в котором переменная заглавная буква, кажется, терпит неудачу один раз. он доходит до слова "тест", что для меня не имеет смысла - кажется, это противоречит тому, что должны делать методы. Я также попробовал это в irb и увидел тот же результат, так что я не думаю, что это ошибка Codewars.

Я все еще относительно новичок в Ruby. Может ли кто-нибудь помочь мне найти то, что я должен упускать из виду, и/или объяснить, как этот код пришел к такому результату?

Обновлять:

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

def weirdcase string
  char = Array.new
  puts "string: #{string}"
  string.split(' ').each do |word|
    word.each_char.with_index do |c, index|
      if index.even?
        char.push(c.upcase)
      else
        char.push(c.downcase)
      end
    end
  end
  puts "char: #{char}"
  puts "char.join(''): #{char.join('')}"
  puts "char.join('space'): #{char.join(' ')}"

end

И вот результаты:

weirdcase
should return the correct value for multiple words
string: This is a test
char: ["T", "h", "I", "s", "I", "s", "A", "T", "e", "S", "t"]
char.join(''): ThIsIsATeSt
char.join('space'): T h I s I s A T e S t
Expected: "ThIs Is A TeSt", instead got: nil

Итак, проблема, с которой я столкнулся сейчас, заключается в том, как правильно объединить слова с пробелами между ними вместо того, что вы видите выше. Я ищу способы правильного доступа к записям массива после их первоначального разделения. Но, конечно, я буду рад любым советам, которые могут быть у кого-нибудь :)


  • Когда ваши строки кода становятся такими длинными, вы должны заменить тернарные операторы операторами if/else. Кроме того, вы должны удалить из этого кода все отвлекающие комментарии и использовать локальную переменную (case) вместо переменной экземпляра (@case). 27.02.2015
  • Куда исчезли ваши места? Это может привести к ответу. 27.02.2015
  • Похоже, вы только нажимаете на свой массив, если определяете, что имеете дело с буквой ([[:alpha:]]). Вам также нужно нажать пробелы. 27.02.2015
  • О, case — это зарезервированное слово, поэтому вы не можете использовать case в качестве имени локальной переменной. В любом случае это было дурное имя; вы должны использовать что-то вроде chars, так как это массив символов. 27.02.2015
  • Добро пожаловать в Stack Overflow. При создании вопроса постарайтесь придумать заголовок, описывающий реальную проблему, которую вы пытаетесь решить. Другой Codewars Oddity никому ничего не говорит об этом вопросе. Это замедляет получение ответов на ваш вопрос и не имеет смысла для других, ищущих ответ на тот же вопрос. 27.02.2015

Ответы:


1

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

string.index(c).even?

Предположим, что c равно 'T'. Тогда string.index(c) всегда будет возвращать индекс первой буквы T в вашей строке, который всегда равен нулю, даже если вы пытаетесь обработать одну из букв T в более поздней части вашей строки.

Чтобы получить индекс символов по мере их повторения, должно работать что-то вроде этого:

string.each_char.with_index do |c, index|
  if index.even?
    #...
  else
    #...
  end
end
27.02.2015

2

Как упоминалось в другом ответе, вызов index(c) является основной проблемой вашего текущего кода. Он вернет первый индекс в строке символа, соответствующего запрошенному (с учетом регистра). Учитывая вашу тестовую строку "this is a test", поскольку первый "t" имеет индекс 0 (четный), ВСЕ (строчные) t будут заглавными, а поскольку первый "s" имеет индекс 3 (нечетный), ВСЕ (строчные) s будут строчными. Тем не менее, похоже, что вы можете немного неправильно понять часть проблемы:

все символы с четным индексом в каждом слове в верхнем регистре, а все символы с нечетным индексом в каждом слове в нижнем регистре.

В своем примере они дают вам это

weirdcase( "Weird string case" );#=> returns "WeIrD StRiNg CaSe"

Если вы внимательно посмотрите на этот пример, со строками из нескольких слов он основан не на абсолютном индексе в строке, а на индексе каждой буквы в каждом слове. Если бы это было первое, вы видите это преобразование (e = четный индекс, o = нечетный индекс)

 eoeoeoeoeoeoeoeoe
"Weird string case" # => WeIrD StRiNg cAsE

Потому что "c" в регистре - нечетный индекс, поэтому он будет строчным. Но из их примера это верхний регистр - каждое слово обрабатывается так, как будто первая буква в слове имеет индекс 0 для этого слова.

Я бы порекомендовал а) написать метод применения странного регистра к каждому слову в строке, б) разбить строку на слова и применить странный регистр к каждому по отдельности, используя только что написанный вами метод, и в) соединить их вместе (дон не забудьте указать символ для их объединения, например, join(' ').

27.02.2015
  • Спасибо за объяснение. Глядя на проблему таким образом, я не удивлен, что это не сработает. Я переписал код и добавил его в этот пост, хотя у меня все еще есть некоторые проблемы... 01.03.2015

  • 3

    Рабочее решение:

    После долгих проб и ошибок мне удалось собрать что-то, что работает:

    def weirdcase string
      arr, char, result = [],[],[]
      arr = string.split(' ').each_slice(string.split(' ').count).to_a.transpose
      arr.each_with_index do |arr_word, index|   
        arr_word.each_with_index do |word, sub_index| 
          word.each_char.with_index do |letter, letter_index| 
            letter_index.even? ? char.push(letter.upcase) : char.push(letter.downcase)
          end
        end
        result.push(char.join(''))
        char.clear
      end
      result.join(' ')
    end
    

    Это неуклюже и, вероятно, не очень эффективно, но это работает. Мне пришлось:

    1. разделить строку на массив "arr" таким образом, чтобы создать подмассив для каждого слова,
    2. перебрать "arr", чтобы получить доступ к словам,
    3. повторять каждое слово, чтобы получить доступ к буквам в его подмассиве,
    4. суб-суб-итерация по буквам и соответственно вверх/вниз по регистру,
    5. передавать измененные буквы по одной в массив "char", где они снова объединялись в слово,
    6. передать слово в массив "результат",
    7. а затем очистите массив «char», чтобы он не объединял слова итеративно, например:

      ["ЭТО", "ЭТО", "ЭТО", "ЭТОСАТЕСТ"]

    Это начиналось с самого начала на каждой итерации, и по ходу дела склеивались все слова. Это было самым трудным для понимания. Я использовал много операторов put для проверки ошибок, и мне пришлось проделать много проб и ошибок, прежде чем я понял это. Я предполагаю, что есть способ заставить его перемещаться от одного элемента массива к другому вместо того, чтобы каждый раз начинать с начала, но из-за разочарования я использовал «char.clear» в качестве обходного пути. Как я уже сказал, это неуклюже. Также не очень эффективно иметь 3 вложенных оператора do или 3 отдельных массива. Может ли кто-нибудь сделать какие-либо предложения по тому, как сделать код более эффективным и/или более Ruby-ишным?

    Если я смогу придумать улучшенную версию, я добавлю ее сюда. И если вам интересно, вот результаты Codewars-Submit-Test:

    weirdcase
    should return the correct value for a single word
    Test Passed: Value == "ThIs"
    Test Passed: Value == "Is"
    Test Passed: Value == "A"
    Test Passed: Value == "TeSt"
    Test Passed: Value == "LoOkS"
    Test Passed: Value == "LiKe"
    Test Passed: Value == "YoU"
    Test Passed: Value == "PaSsEd"
    should return the correct value for multiple words
    Test Passed: Value == "ThIs Is A TeSt"
    Test Passed: Value == "LoOkS LiKe YoU PaSsEd"
    Test Passed: Value == "ThIs Is ThE FiNaL TeSt CaSe"
    Test Passed: Value == "JuSt KiDdInG"
    Test Passed: Value == "Ok FiNe YoU ArE DoNe NoW"
    
    01.03.2015
  • Хорошее решение. Комбинация each_char.with_index довольно умна. Вам определенно не нужно так много уровней итерации или очень сложная строка, разбитая на массивы массивов отдельных слов. Вот еще одна версия, которая может быть немного более похожей на Ruby: repl.it/cZs/1 02.03.2015
  • Новые материалы

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

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

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..