Наш чатик

Телеграм чат начинающих программистов. Общаемся и помогаем друг другу

Если ссылка не открывается, можно найти нас в поиске по чатам @rubyrush или пойти другим путем

ARGV и игрушка-тест

В этом уроке мы используем всё, чему научились в прошлых уроках. Напишем игрушку тест «Ревнивы ли вы?» и научимся ещё одному способу ввода данных в программу.

Мы закрепим старые знания и узнаем, как передавать аргументы в наши программы на Ruby с помощью массива ARGV. Узнаем, что метод gets лучше вызывать с помощью специального объекта STDIN, а также поймем как работают команды downcase и конструкция #{} для вставки значений в строки.

План урока

  1. Как получить доступ в программе к параметрам запуска из командной строки.
  2. Простая программа тест «Ревнивы ли вы?». В ней используются массивы, циклы, ввод данных из консоли и из аргументов командной строки.

Ревность

Аргументы, передаваемые при запуске из командной строки

Раньше мы выводили версию нашего Ruby командой

ruby -v

Мы на самом деле передавали этой программе аргумент (ключ) -v, который наш компилятор воспринимает как запрос своей версии.

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

ruby program.rb агрумент1 аргумент2 аргумент3

Аргументы разделяются пробелами и компилятор Ruby для удобства программистов уже аккуратно сам их складывает в массив ARGV, таким образом, если мы напишем программу hello.rb.

puts "Hello, " + ARGV[0]

И запустим её вот так

ruby hello.rb World

То в консоли появится

Hello, World

Вспомним, что + между двумя строками объединяет их в одну.

Конвертирование строк в кодировку UTF-8 (для Windows)

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

Поэтому каждый раз, когда вы берёте аргумент с помощью команды gets или из массива ARGV, не забывайте конвертировать его в UTF-8. Для учебных же целей рекомендуем вам ограничиться только английскими строками.

Вот эта конструкция в программе проверяет выполняется ли она на Windows и если в программу передали аргумент, запишет его значение в переменную argument в правильной кодировке UTF-8

argument = ARGV[0]

if (Gem.win_platform? && ARGV[0])
  argument = argument.dup
    .force_encoding("IBM866")
    .encode("IBM866", "cp1251")
    .encode("UTF-8")
end

Пока не пытайтесь понять смысл всех операций в этой строке — используйте ее как есть (если, конечно, у вас возникли проблемы с читаемостью русских букв в программах). Только поменяйте название переменных на те, что вам нужны.

Проверка аргумента

Очень важный момент, который возникает у вас каждый раз, когда вы ждёте какой-то информации от пользователя, связан с тем, что пользователь может об этом и не знать. Поэтому всегда, когда вы считаете, что пользователь должен ввести аргумент запуска, проверяйте это. Если, например, ваша программа не может без него работать, то прервите её выполнение с помощью команды abort.

if (ARGV[0] == nil)
  abort "Нужен какой-нибудь аргумент!"
end

Если пользователь ничего не ввёл после имени файла программы, то в ARGV[0] будет содержаться nil и программа войдёт в тело проверки if: выполнит команду abort и тут же завершится, сообщив пользователю, что нужно указать какой-то аргумент.

Тест «Ревнивы ли Вы?»

Как обычно написание программы начинается с постановки задачи.

Сформулируем: «Написать программу, которая тестирует пользователя и выдаёт ему результат в зависимости от его ответов».

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

Мы будем писать нашу программу в файле jealous_test.rb, который по традиции создадим в нашей папке с:\rubytut\lesson8.

Для этого мы создадим два массива — один для вопросов и один для ответов. Мы уже умеем создавать массивы со строками.

Приступим. Во-первых, мы создадим два массива questions и result, где будем хранить вопросы теста и результат, который нужно будет вывести пользователю по окончанию.

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

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

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

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

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

  string = "abc" +
  "def"

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

Теперь нам нужна переменная, которая будет считать количество ответов «да», мы назовём её yes_answers. Объявим ее в начале программы зададим начальное значение – ноль.

yes_answers = 0

Наконец, приступаем к основному циклу программы. Так как у нас есть массив вопросов, каждый из которых нам надо задать пользователю, удобнее воспользоваться циклом for-in:

for item in questions do
  # делаем что-то с вопросом item
end

В теле цикла мы во-первых, пишем вопрос для пользователя на экране:

puts item

Во-вторых, спрашиваем у него ответ, который должен быть либо yes либо no.

Как мы уже говорили, если пользователю нужно что-то ввести, мы включаем режим осторожности и проверяем введённые им данные: вдруг он ошибся или опечатался.

Поэтому здесь мы будем использовать цикл while с нужным нам условием

user_input = nil

while (user_input != "yes" and user_input != "no")
  puts "Введите yes или no"
  user_input = STDIN.gets.chomp.downcase
end

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

Обратите внимание, что вместо gets мы используем STDIN.gets — это новый для вас способ. И это более правильный способ. Только он сработает в программах, которые работают с аргументами командной строки.

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

Обратите внимание, что мы применяем к введённому пользователем тексту два метода во-первых, мы, как мы это часто делаем, обрезаем последний символ с помощью chomp, чтобы убрать символ переноса строки (Enter).

Метод строки downcase

А второй метод downcase мы используем, чтобы перевести все буквы, введённые пользователем в нижний регистр, чтобы помимо yes пользователь также мог ввести YES или Yes, а для программы не было разницы

Ещё раз:

puts "Some Words".downcase

выведет в консоль

some words

Наконец, если пользователь ввёл yes, то мы увеличиваем число ответов «да» yes_answers

if (user_input == "yes")
  yes_answers += 1
end

Вставка переменной в строку

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

puts "\n #{name}"

Обратите внимание на конструкцию #{name}: эта штука — альтернатива нашим любимым плюсикам для «склеивания строк». Раньше, если нужно было собрать строку из переменных и других строк, мы писали

gde = "ГДЕ"
my_string = "АБВ" + gde + "ЁЖЗ"

Это же самое можно сделать и с помощью конструкции #{abc}

my_string = "АБВ#{gde}ЁЖЗ"

Это во-первых, компактнее, во-вторых, нагляднее. К сожалению, такая конструкция встречается далеко не в каждом языке программирования. В Ruby она, к счастью, есть.

Оператор ветвления if-elsif-else

Результат зависит от количества ответов yes. Поэтому мы строим витиеватую конструкцию if-elsif-else.

if (yes_answers >= 10)
  puts results[0]
elsif (yes_answers >=5)
  puts results[1]
else
  puts results[2]
end

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

В отличие от else, после elsif тоже должно быть записано какое-то условие.

Условие, которое стоит после elsif, проверяется только если не выполнилось условие if. Если условие после elsif выполнилось, программа будет исполнять инструкции сразу после elsif, а как только встретит else (или другой elsif) — выйдет из ветвления. А если и условие после elsif не выполнилось, то программа пойдёт по пути else.

Таким образом, в нашем случае если число ответов «да» больше или равно 10, то выведется первый (с номером 0) вариант результата.

Если число ответов «да» меньше 10, но больше либо равно 5, то второй вариант результата (с индексом 1) и, наконец, во всех других случаях (если число ответов «да» от 0 до 4 включительно) программа выведет второй вариант результата.

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

cd c:\rubytut\lesson8
ruby jealous_test.rb Василий

Запустите вашу программу и пройдите тест. Убедитесь, что всё работает, как надо и проверьте свой уровень ревности.

Также учтите, что оператор if может содержать только одну «ветвь» else, но сколько угодно ветвей elsif (каждая со своим условием). Причем если не выполнилось основное условие if — будет выбран первый сверху вниз elsif, условие которого выполняется.

Важно помнить об этом (что выбор ветвей идет сверху вниз), если у вас есть несколько путей (несколько 'elsif'), а условия некоторых из них пересекаются (то есть могут быть верны одновременно).

Итак, в этом непростом уроке мы узнали много нового, а главное лучше поняли то, что уже изучали.

Мы узнали, как передавать аргументы в наши программы с помощью массива ARGV, выяснили, что метод gets лучше вызывать с помощью объекта STDIN и узнали как работают команды downcase и конструкция #{} для вставки значений в строки. А также научились ветвлению программы с помощью оператора if-elsif-else.

В следующем уроке нас ждёт очень важная тема: методы и функции. Мы узнаем, как создавать маленькие подпрограммы внутри наших программ, что такое методы и как их писать.