Как мы можем установить переменную после того, как пользователь выберет из меню HighLine в Ruby?

#ruby #string #variables #menu #highline

#ruby #строка #переменные #меню #highline

Вопрос:

Документация HighLine показывает, что мы можем отобразить строку после того, как пользователь выберет опцию из списка, следующим образом:

 choose do |menu|
  menu.prompt = "Please choose your favorite programming language?  "

  menu.choice(:ruby) { say("Good choice!") }
  menu.choices(:python, :perl) { say("Not from around here, are you?") }
end
  

Как мы можем установить переменную в дополнение к отображению текста (или вместо него)? Замена say("Good choice!") на variable = 1 не сработала и вместо этого вернула ошибку «неопределенная локальная переменная или метод».

Ответ №1:

Локальные переменные уничтожаются, когда метод / блок / процедура завершает выполнение. Но вы можете сделать так, что variable это не локальная переменная блока:

 variable = nil

choose do |menu|
  variable = 1
  menu.prompt = "Please choose your favorite programming language?  "

  menu.choice(:ruby) { say("Good choice!") }
  menu.choices(:python, :perl) { say("Not from around here, are you?") }
end

puts variable
  

Блок создает то, что называется closure , что означает, что он может видеть переменные, которые существовали за пределами блока, когда блок был СОЗДАН. С другой стороны, блок не может видеть переменные за пределами блока во время его выполнения — даже если переменные имеют те же имена, что и переменные, которые может видеть блок. Например,

 def do_stuff(amp;block)
  x = 1
  block.call
end

x = 10
do_stuff {puts x}

--output:--
10
  

Комментарии:

1. Вау, это было просто. Итак, если я правильно понимаю, все переменные, созданные в блоке, будут уничтожены в конце, потеряв все сохраненное ими значение. Однако, если переменная была создана до блока, она сохранит любое значение, присвоенное ей внутри блока?

Ответ №2:

TL; DR ответ заключается просто в том, что вам нужно определить переменную, доступную за пределами области вашего блока.

Прежде всего, ознакомьтесь с документацией API:

http://rubydoc.info/gems/highline/HighLine#choose-instance_method

Вы увидите def choose( *items, amp;details ) , что метод choose принимает аргументы любой длины, а затем также блок. Вы видите, что блок do ... end передается вызову choose:

 choose do |menu|
  menu.choice(:meow) {|q| q = 'foo'}
end
  

Хорошо, итак, мы видим другой метод, который choice : http://rubydoc.info/gems/highline/HighLine/Menu#choice-instance_method

 # File 'lib/highline/menu.rb', line 136

def choice( name, help = nil, amp;action )
  @items << [name, action]

  @help[name.to_s.downcase] = help unless help.nil?
  update_responses  # rebuild responses based on our settings
end
  

Теперь давайте разберем, .choice(:meow) {|q| q = 'foo'} используя то, что мы видим из choice . Он ожидает имя (:meow) , по умолчанию для справки используется значение nil, если оно не указано (поэтому в моем примере вызова оно равно nil), и, наконец, принимает другой блок, который является {|q| q = 'foo'} .

Мой пример оценивается как:

 1. meow
?  1
 => "foo"
q
=> NameError: undefined local variable or method `q' for main:Object
  

Почему? потому что q определен только в пределах области действия передаваемого блока. Вы не сможете получить к ней доступ после вычисления этой процедуры. Итак, что вам нужно сделать, это:

 1.9.3-p448 :015 > choose do |menu|
1.9.3-p448 :016 >     menu.choice(:meow) { @foo = 'foo'}
1.9.3-p448 :017?>   end
1. meow
?  1
 => "foo"
1.9.3-p448 :018 > @foo
 => "foo"