Как проверить, определена ли переменная в Ruby

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


Ruby предоставляет удобное ключевое слово defined?(expression), которое проверяет, относится ли выражение к чему-либо распознаваемому. Выражение может быть объектом, инициализированной переменной, именем метода и т.д. Если Ruby не может разрешить выражение, он возвращает nil. В противном случае возвращается строка, описывающая выражение.

Вот несколько примеров использования defined? с различными типами выражений. Обратите внимание, что переменная, установленная в nil, все равно инициализируется и распознается ruby.

RSpec.describe 'Defined' do
  it 'tests if the local variable is defined' do
    name = 'Akshay'

    expect(defined? name).to eq('local-variable')
    expect(defined? b).to eq(nil)
    expect(defined? nil).to eq('nil')
    expect(defined? String).to eq('constant')
    expect(defined? 1).to eq('expression')
  end

  it 'ensures that a variable set to nil is still recognized' do
    name = nil
    expect(defined? name).to eq('local-variable')
  end
end
Вход в полноэкранный режим Выход из полноэкранного режима

Использование с условным присваиванием

Иногда вы хотите лениво оценить некоторый код только один раз. То есть, ничего не делать, если переменная существует, но инициализировать ее, если нет. Идиоматический подход ruby заключается в использовании оператора ||=.

def result
  @result ||= calculate_result
end

def calculate_result
  puts '>>> heavy calculation here.. should happen only once'
  100
end

it 'lazy-evaluates the calculate_result operation once' do
  expect(result).to eq(100)
  expect(result).to eq(100)
end

# Output

>>> heavy calculation here.. should happen only once
Войти в полноэкранный режим Выход из полноэкранного режима

Только убедитесь, что вы не используете его с операцией, которая может вернуть nil или булево значение false в качестве результата. В противном случае он будет вызывать calculate_result каждый раз, устраняя преимущество оператора ||=.

Например, если вы измените метод calculate_result выше, чтобы он возвращал false (или nil) вместо 100, ruby будет вызывать calculate_result каждый раз, когда вызывается result.

def calculate_result
  puts '>>> heavy calculation here.. should happen only once'
  false # or nil
end

# Output

>>> heavy calculation here.. should happen only once
>>> heavy calculation here.. should happen only once
Вход в полноэкранный режим Выход из полноэкранного режима

В таких случаях пригодится метод defined?. Измените метод result так, чтобы он сначала проверял, определена ли переменная @result.

def result
  return @result if defined? @result
  @result = calculate_result
end
Вход в полноэкранный режим Выход из полноэкранного режима

Не используйте defined? для проверки хэш-ключей

Распространенной ошибкой является использование defined? для проверки того, определен ли хэш-ключ. Например,

def check_hash_key
  hash = {}
  defined?(hash['key']) ? 'unexpected!' : 'not defined'
end

it 'does not return false for non-existing hash key' do
  expect(check_hash_key).to eq('unexpected!')
end
Войти в полноэкранный режим Выйти из полноэкранного режима

Это потому, что он возвращает строку method, которую ruby оценивает как true в булевом выражении.

it 'returns method for non-existing hash key' do
    data = {}
    expect(defined? data['key']).to eq('method')
end
Войти в полноэкранный режим Выход из полноэкранного режима

Идиоматическим решением ruby для проверки существования ключа в хэше является использование любого из следующих методов: has_key?, key?, include?, или member?.

data = {}
expect(data.key?('key')).to eq(false)
Вход в полноэкранный режим Выйти из полноэкранного режима

Используйте круглые скобки при использовании defined?

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

it 'has low precedence' do
    result = 10
    expect(defined? result && result > 0).to eq(true)
end

# Fails!
# expected: true
# got: "expression"
Войти в полноэкранный режим Выйти из полноэкранного режима

Добавление круглых скобок приводит к лучшей проверке, к тому же она очень наглядна. Следующий тест проходит на ура.

it 'has low precedence' do
    result = 10
    expect(defined? result && result > 0).to eq('expression')
    expect(defined?(result) && result > 0).to eq(true)
end
Войти в полноэкранный режим Выход из полноэкранного режима

Оцените статью
devanswers.ru
Добавить комментарий