Rubyrush: ARGV и программа «Тест на ревность»

Ruby для новичков [Rubyrush]

На шаге №32 обещают рассказать про еще один способ ввода данных в программу. После просмотра не сразу смог понять суть и пересмотрел на утро. Оказалось всё гораздо проще, чем я думал.

Я ждал какую то сложность, а тут речь шла о простом запуске программы Ruby, дополненную параметрами — аргументами. Но как выяснилось дальше, при написании программ, которые работают с данными через ARGV есть нюансы. Например в работе метода gets. Об этом и многом другом в сегодняшней статье.

Как узнать версию установленной Ruby

Ранее, мне показывали как проверить версию программы Ruby, после установки на комп: ruby -v

В терминале, при вводе этой команды мне возвращалась версия программы + дополнительная информация.

Rubyrush: ARGV и программа "Тест на ревность"
Запрос версии Ruby установленной на компьютер.

Как передавать данные в программу через параметры вызова

Аналогично запросу версии программы, в Ruby можно передавать и другие аргументы, например вводить данные. Но как это сделать? Давай разбираться.

Для начала нам нужно подготовить сценарий — написать небольшую программу, в которой мы объявим переменную, в которой сохраним массив. В массив мы будем складывать все аргументы, которые будут указаны при запуске программы. Но что бы не запутаться, будем брать в работу только самый первый аргумент, а остальные, если вдруг будут вводиться — просто проигнорируем. не забывай, что первый элемент в массиве имеет индекс 0! Ноль, Карл! не единичка! 🙂 Всё время про это забываю.

Наш сценарий будет выглядеть следующим образом: argument = ARGV[0]

В массив ARGV (от ARGument Values) складываются значения аргументов командной строки.

Например, если написать выражение типа:

ruby main.rb 1 2 some text

То массив ARGV будет таким ["1", "2", "some", "text"]:

puts ARGV.inspect

Запуск:

$ ruby main.rb 1 2 some text
["1", "2", "some", "text"]

Обрати внимания, что в массиве нет цифр! Тут только строки, а каждый пробел начинает новую строку.

Это очень удобно и применяется в большинстве более-менее сложных консольных программ, в которых есть передача параметров при запуске. Например, путь к файлу, из которого необходимо считать данные.

Вернёмся к нашей программе. Теперь, когда мы запрограммировали сохранение входящих аргументов, давайте пропишем реакции программы на эти данные. В уроке предлагается ситуация с вариантами приветствия. Давайте так и поступим:

Запрограммируем сценарий, при котором мы получаем данные от пользователя. Дальше пишем условия:

  • если пользователь в качестве аргумента вводит своё имя, то мы его приветствуем,
  • а если он пишет ругательство (условно пусть это будет слово «дурак»), то мы обзываем его этим же словом в ответ.

Сценарий на языке ruby будет выглядеть вот так:

argument = ARGV[0]

if (argument == "дурак")
  puts "Сам ты дурак!"
else
  puts "Приветствую тебя, #{argument}"
end

В строке вывода текста на экран я использую интерполяцию — подстановку данных, сохраненных в переменой. Эти данные являются строкой и выводятся при помощи конструкции типа #{...}.

Объект STDIN (и его метод gets)

Это тоже новинка сегодняшнего урока! Этот метод используется для для работы с аргументами командной строки!

STDIN — это специальный объект, который содержит ссылку на консоль, откуда пользователь вводит данные. 

Метод .downcase

downcase — это метод применяется к строкам и позволяет перевести все символы объекта в нижний регистр. Он поможет нам привести входящие данные к единообразному виду и избежать сюрпризов.

Позже я немного изучил этот метод и выяснил, что в ruby есть несколько подобных методы downcaseupcase или capitalize для изменения регистра букв в строках:

'i am a string'.upcase
 => "I AM A STRING"
 
'I AM A STRING'.downcase
 => "i am a string"
 
'i am a string'.capitalize
 => "I am a string"

Ruby: Чем gets отличается от STDIN.gets?

Если написать в руби просто gets, то будет вызван метод Kernel#gets, а этот метод в руби устроен довольно интересно. Если при запуске программы был передан аргумент (который позже можно получить в программе из массива ARGV), то он воспримет его как путь к файлу и попытается этот файл прочитать, а у пользователя в консоли ничего не просит.

input = gets.chomp

puts input

Запуск:

$ ruby main.rb
Привет!
Привет!

$ ruby main.rb Привет
Traceback (most recent call last):
	2: from main.rb:1:in `<main>'
	1: from main.rb:1:in `gets'
main.rb:1:in `gets': No such file or directory @ rb_sysopen - Привет (Errno::ENOENT)

Чтобы этого избежать, можно обратиться к методу gets объекта STDIN.

STDIN — сокращение от STandarD INput — в данном случае консоль. Если вызвать метод STDIN.gets, то руби вызовет этот метод у объекта стандартного ввода и будет совершенно точно читать именно из консоли, не отвлекаясь на ARGV.

input = STDIN.gets.chomp

puts input

Запуск:

$ ruby main.rb Привет
Досвидули!
Досвидули!

Правило буравчика: если ваша программа использует ARGV, вместо gets необходимо использовать STDIN.gets, если не использует — можно оставить gets, но если везде замените на STDIN.gets хуже не будет.

Пишем простой тест «Ревнивый ли вы?»

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

Постановка задачи

Есть ряд вопросов, на которые нужно ответить да или нет. По итогу в зависимости от количества положительных ответом нужно показать один из результатов теста.

напишем программку, которая поочерёдно задаёт закрытые вопросы (на которые можно ответить только «да» или «нет»), а потом выводит один из результатов теста в зависимости от количества ответов «да».

Реализация поставленной задачи

Шаг №1 — создаём массивы и сохраняем в них вопросы теста и итоговые результаты

Для выполнения задачи мы создадим ДВА массива ,в которых сохраним варианты вопросов и результатов теста:

questions = [
    "Если ваш партнер бросает взгляд на незнакомую женщину, вы устраиваете ему скандал прямо на улице?",
    "Если ваш партнер опаздывает на ужин, вы уверены, что он был с другой?",
    "Вы расспрашиваете его о работе, о коллегах?",
    "Вы считаете, что каждую свободную минуту должны проводить вместе?",
    "Он для вас — свет в окошке?",
    "Случается ли вам проверять его корреспонденцию и рыться в его вещах?",
    "Чем чаще он говорит о своих чувствах, тем меньше вы верите?",
    "Вы хотите, чтобы он интересовался только тем, чем интересуетесь вы?",
    "Вы всегда спрашиваете у него, куда он ходит и с кем встречается?",
    "Если вы на него обижены, то молчите по нескольку дней?",
    "Вас мучают мысли о его бывшей возлюбленной?",
    "Он утверждает, что не ревнует вас, потому что доверяет. Для вас это означает, что любовь прошла?"
]

results = [
  # 10 и более ответов «да»
  "Вы болезненно ревнивы. Не думайте, что если избранник вас любит, " +
  "то он автоматически становится вашей собственностью. Вы считаете себя непривлекательной " +
  "и боитесь, что он бросит вас ради какой ни будь красавицы. Вы ни в чем не уверены, особенно " +
  "в нем. Задумайтесь над этим, потому что нельзя быть настолько ревнивой и агрессивной, это " +
  "может привести к конфликтам и даже к разрыву отношений.",

  # 5–9 ответов «да»
  "Ваша ревность действует на вас мобилизующе, но не она одна управляет вашим поведением. " +
  "В минуту слабости случается и вам устраивать скандалы. Но, успокоившись, вы понимаете, " +
  "что для вашей ревности не было никаких оснований.",

  # Менее 5 ответов «да»
  "Вам совершенно незнакомо чувство ревности. Но тревога и беспокойство могут накапливаться " +
  "со временем. Вы должны решать волнующие вас проблемы со своим партнером."
]

Мне понравилась их демонстрация того как можно организовать хранение длинных строк текста, при помощи конкатенации = оператора сложения, применимого к объектам типа СТРОКА. Они просто делят текст на читабельные фрагменты, оборачивают в кавычки и дополняют плюсом, для дальнейшего автоматического объединения. В конце текстового блока ставят запятую, что бы отделить один элемент массива от другого. Учту это в будущих проектах.

Для переноса строк мы пользуемся уже полюбившимся нам плюсиком. Мы уже привыкли, что плюсик складывает не только числа, но и строки и поэтому мы можем написать

  string = "abc" +
  "def"

и в переменной string будет лежать после этого "abcdef".

Шаг №2 Объявляем переменную, для хранения количества положительных ответов пользователя на вопросы теста

По умолчанию переменной сохраняем значение 0

Шаг №3 Пишем основной цикл нашего теста, который проверяет какой ответ ввёл пользователь

Для этого используем цикл for. Начинаем с того что задаем пользователю вопрос. Это нужно для того, что бы сказать пользователю что мы от него хотим.

Затем пользователь отвечает на вопрос и вводит свой ответ.

Мы проверяем входящие от пользователя данные на соответствие одному из двух возможных значений, а если данные не соответствуют параметрам — делаем вывод, что ответ не верный и просим пользователя снова ввести ответ.


for item in questions do
  puts item

  user_input = nil

end

Внутри основного цикла заводим переменную user_input, в которой будем сохранять ответ пользователя. начальное значение у этой переменной будет nil. Думаю ты понимаешь зачем, верно? Да, правильно подумал-для того что бы наша программа не выдавала ошибок 🙂 если объявлена переменная, значит должно быть присвоено и какое то начальное значение.

Для проверки ответа пользователя внутри основного цикла мы заводим еще один цикл while внутри которого программируем условия проверки. Цикл будет повторяться до тех пор пока выполнение условия возвращает нам true.

Такой сценарий вполне логичен, потому что вариантов правильно ответа всего два, и нам проще прописать условие проверки, сравнив с двумя вариантами верного ответа, нежели с кучей вероятных сценариев.


for item in questions do
  puts item

  user_input = nil
  while (user_input != 'да') && (user_input != 'нет')
    puts 'введите или "да" или "нет" без каких либо лишних символов.'
    user_input = STDIN.gets.chomp.downcase
  end
end

И тут ребята показали в уроке тот самый нюанс с работой входящих данных по средствам ARGV — привычный нам ранее метод gets может не сработать при работе с данными передаваемыми в программу при помощи ARGV. Поэтому необходимо использовать специальный объект STDN и дополнять его методом gets.

Ну а дальше мы действуем как обычно. обрезаем нежелательный перенастройки, который автоматом сохраняется в данных которые вводят в программу через консоль, и для перестраховки переводим все символы введённых данных в нижний регистр по средствам метода downcase.

Отлично! промежуточный результат работы программы нам подходит. Если условие проверки не выполнено — мы выходим из цикла и двигаемся дальше по сценарию.

А дальше по программе у нас шаг №4.

Шаг №4 Действия программы в зависимости от ответа пользователя условный оператор if

При каждом положительном ответе пользователя на вопрос мы прибавляем к значению сохранённому в переменной user_answers единичку. Счетчик увеличивается на 1. По итогу завершения тестирования количество положительных ответом будет посчитано и мы сможем показать результат тестирования в программе.

user_answers = 0

questions.each do |item|
  puts item

  user_input = nil
  while (user_input != 'да') && (user_input != 'нет')
    puts 'введите или "да" или "нет" без каких либо лишних символов.'
    user_input = STDIN.gets.chomp.downcase
  end

  user_answers += 1 if user_input == 'yes'
end

Шаг5 Вывод результатов тестирования

Для вывода текста на экран будем использовать старый добрый метод puts и интерполяцию:

puts "\n #{name}"
puts "\n Ваш результат тестирования (ответов ДА - #{user_answers}):"

А для вывода результатов тестирования запрограммируем условия при помощи оператора if:

if user_answers >= 10
  puts result[0]
elsif user_answers >= 5
  puts result[1]
else
  puts result[2]
end

Юрий Ронин