Невозможно скопировать строку в новый массив в Ruby - PullRequest
1 голос
/ 17 июня 2020

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

`block in substrings': undefined method `<<' for nil:NilClass

Я подтвердил, что index в методе не nil, распечатав его. Я также сделал index == nil для двойной проверки.

Что мне здесь не хватает?

Заранее спасибо за вашу помощь!

new_array = []

def substrings(word, array)

  new_array = array.each do |index|

    if index.include? (word)

      p index
      p index == nil

      new_array << index

    end
  end
end


dictionary = ["below", "down", "go", "going", "horn", "how", "howdy", "it", "i", "low", "own", "part", "partner", "sit"]

substrings("i", dictionary)

Ответы [ 3 ]

1 голос
/ 17 июня 2020

По сути, вы комбинируете два разных способа решения этой проблемы. Первый - присвоить new_array результат цикла через массив, но в этом случае переменная new_array недоступна для использования внутри блока.

Таким образом, вы можете либо сначала создать переменную , например,

new_array = []

array.each do |index| 
  if index.include?(word)
    new_array << index
  end
end

В качестве альтернативы вы можете использовать метод под названием reduce, который использует более функциональный подход к программированию. Это может выглядеть так:

new_array = array.reduce([]) do |arr, index| 
  if index.include?(word)
    arr << index
  else
    arr
  end
end

Что делает reduce, так это то, что аргумент блока arr всегда устанавливается на значение, возвращаемое предыдущим выполнением блока. это может сделать синтаксис немного длиннее, чем должен быть, поэтому Ruby также имеет альтернативный подход к сокращению, называемый each_with_object, который делает то же самое, но путем изменения той же переменной, вместо того, чтобы требовать возвращаемого значения. На самом деле я предпочитаю этот способ и решаю его так.

new_array = array.each_with_object([]) do |index, arr| 
  arr << index if index.include?(word)
end
0 голосов
/ 17 июня 2020

Я проверил, что index в методе не nil, распечатав его. Я также сделал index == nil для двойной проверки.

Что мне здесь не хватает?

Не index это nil, а new_array. В сообщении об ошибке написано:

undefined method `<<' for nil:NilClass

и имеется ссылка на строку new_array << index. Здесь << - метод, а new_array - получатель. И по какой-то причине new_array равно nil.

Вы, вероятно, ожидали, что new_array будет [], потому что вы явно указали new_array = []. Но у методов есть своя собственная область видимости локальных переменных. Если вы определяете локальную переменную вне метода, она не будет доступна внутри или наоборот.

Обычно при обращении к неопределенной переменной вы получите:

undefined local variable or method `new_array'

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

  new_array = array.each do |index| 
  ^^^^^^^^^^^

Когда Ruby встречает эту строку, он немедленно создает локальную переменную new_array с начальным значением nil. (локальные переменные создаются, когда строка анализируется, а не когда происходит присваивание)


Чтобы получить ожидаемый результат, вам нужно переместить new_array = [] в метод, получить избавиться от присваивания new_array = array.each { ...} и вернуть new_array в конце:

def substrings(word, array)
  new_array = []

  array.each do |index|
    if index.include?(word)
      new_array << index
    end
  end

  new_array
end

Имена переменных немного произвольны, возможно, даже вводят в заблуждение. Наличие index.include?(word) выглядит так, как будто вы сравниваете числовой индекс со строкой. Я бы использовал что-то вроде этого:

def substrings(substring, words)
  result = []

  words.each do |word|
    if word.include?(substring)
      result << word
    end
  end

  result
end

По коду вы можете включить массив в l oop через each_with_object, который также вернет массив:

def substrings(substring, words)
  words.each_with_object([]) do |word, result|
    if word.include?(substring)
      result << word
    end
  end
end

Однако выбор элементов на основе условия является настолько распространенной задачей, что Ruby предоставляет специальный метод select - вам просто нужно вернуть true или false из блока, чтобы указать, следует ли выбирать элемент:

def substrings(substring, words)
  words.select do |word|
    word.include?(substring)
  end
end

0 голосов
/ 17 июня 2020

Я хотел бы немного расширить (правильный) ответ, данный @DanneManne: хотя верно, что вы можете получить доступ к локальным переменным из внешних блоков из внутреннего блока, вы не можете сделать это в пределах def , и это не нужно в вашем примере, потому что вы можете инициализировать new_array внутри тела вашего метода и вернуть его как результат. Но в случае, если вам когда-либо действительно понадобится такая конструкция, действительно есть обходной путь:

Итак, это НЕ работает:

a=5
def f
  puts a; # WRONG. a is not known here
end

, и это работает не так, как вы, кажется, ожидаете:

a=5
def f
  a=6
end

puts a # prints 5
f
puts a # prints 5 again

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

a=5
define_method(:f) do
  puts a;  # OK, refers to outer variable a
end

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

f;
a=6;
f

5 и 6 печатаются в таком порядке.

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