Использование заглавных слов в массиве, Ruby

Я просматриваю вопросы Ruby Prep от App Academy и хочу знать, почему это решение работает. Похоже, что массив слов никогда не изменяется, и тем не менее метод работает. Это глюк матрицы или это прямо у меня под носом?

def capitalize_words(string)
  words = string.split(" ")

  idx = 0
  while idx < words.length
    word = words[idx]
    word[0] = word[0].upcase
    idx += 1
  end

  return words.join(" ")
end

person Styx_    schedule 06.03.2015    source источник
comment
Разместите соответствующий код   -  person August    schedule 06.03.2015
comment
Опубликовано, извините, это мой первый пост, и форматирование заняло некоторое время. @Август   -  person Styx_    schedule 06.03.2015


Ответы (3)


String#[]= — это изменяющая операция . Чтобы проиллюстрировать это, используйте краткий выдержка из вашего кода:

word = "foo"
word[0] = word[0].upcase  # <-- verbatim from your code
word #=> "Foo"

word по-прежнему является тем же самым объектом, который содержится в массиве words (массивы просто содержат ссылки на объекты, а не данные внутри них), но он был мутирован на месте. Как правило, лучше избегать мутаций, когда это возможно, поскольку это делает неочевидным то, что происходит (как вы можете видеть).

Ваш код также можно написать более лаконично, используя map & capitalize (и без каких-либо мутаций) :

string.split(' ').map(&:capitalize).join(' ')
person Andrew Marshall    schedule 06.03.2015
comment
Забудьте об этом, я не могу выразить то, что пытаюсь сказать в этом комментарии. -_- - person Styx_; 06.03.2015

Метод работает, потому что word содержит ссылку на позицию в массиве. Итак, когда вы назначаете:

word = words[idx]

Вы просто используете word в качестве сокращения для работы с этим элементом массива, который изменяется:

word[0] = word[0].upcase

--

Кроме того, если вы хотите вернуться к этому ответу после изучения Ruby, вот упрощенная версия метода:

def capitalize_words(string)
  string.split.map(&:capitalize).join(' ')
end
person nicosantangelo    schedule 06.03.2015
comment
Это... действительно странно. Я бы подумал, что мне нужно будет установить words[idx] = word. Это особенность рубина или что? Есть ли для этого название, чтобы я мог изучить его немного больше? - person Styx_; 06.03.2015
comment
@Styx_ Это называется мутация. Как сказано в моем ответе, массивы просто содержат ссылки на объекты, поэтому, когда вы изменяете объект, он «меняется» везде, где есть ссылка на него. Если вы вызываете freeeze для объекта в Ruby, его нельзя изменить. Почти каждый язык (к сожалению) подвержен мутациям, за исключением Clojure. - person Andrew Marshall; 06.03.2015

word = word[idx] создает копию ваших данных. Затем он изменит эту копию вместо слов в исходном массиве.

Простым решением будет:

def capitalize_words(string)
  words = string.split(" ")

  idx = 0
  while idx < words.length
    words[idx][0] = words[idx][0].upcase
    idx += 1
  end

  return words.join(" ")
end
person Eduardo Bautista    schedule 06.03.2015