Rubyrush: Массивы и поток выполнения программы в Ruby

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

Сегодня началась новая большая тема. Фраза «Поток выполнения» меня будоражит. Посмотрим чему научат меня в этот раз 😉

Массив

Как вывести содержимое массива

Для этого достаточно присвоить элементам массива тип Строка и вывести по средствам команды puts:

games = ["Dark Souls", "Aion", "CS GO", "FEAR"]
puts "Массив содержит: " + games.to_s
Rubyrush: Массивы и поток выполнения программы в Ruby
Результат вывода содержимого массива.

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

Как поместить объект программы в массив

У нас есть два варианта:

  • Клювики << — подходит, когда нужно положить в массив только один объект.
  • Команда push — позволяет поместить в массив сразу несколько объектов. Важно помнить, что элементы, добавляемые в массив добавляются в том же порядке, в котором мы их прописываем в команде push.

Порядок элементов в массиве очень важен!

games = ["Dark Souls", "Aion", "CS GO", "FEAR"]
puts "Массив содержит: " + games.to_s

games << "Half-Life"
puts "Массив содержит: " + games.to_s

games.push("Northgard", "Watch_Dogs")
puts "Массив содержит: " + games.to_s
Rubyrush: Массивы и поток выполнения программы в Ruby
Пример нескольких способов добавления объектов в массив.

Как достать элемент массива

Для этого используются квадратные скобки. В них указывают порядковый номер элемента массива, из которого требуется достать элемент. Тут важно понимать, что элементы в массивах нумеруются начиная с 0, а не с 1.

Отрицательный индекс предполагает отсчет с конца массива, то есть индексу -1 соответствует последний элемент массива, -2 — предпоследний, и так далее.

Rubyrush: Массивы и поток выполнения программы в Ruby
Демонстрация способа вывода определённого элемента массива в строке.

Как удалить элемент из массива

Для удаления элемента массива используется два способа:

  • Удаление по точному значению элемента массива
  • Удаление по порядковому номеру.

Удаление по точному значению элемента массива осуществляется при помощи команды delete(«»). В качестве аргумента этой функции передаётся название того элемента, который мы хотим удалить.


games.delete("Aion")
puts "Массив содержит: " + games.to_s
Rubyrush: Массивы и поток выполнения программы в Ruby
Демонстрация способа удаления элемента из массива.

Удаление по порядковому номеру элемента массива осуществляется по средствам команды .delete_at(0). при этом в качестве аргумента команды передаётся порядковый номер того элемента массива, который требуется удалить.

games.delete_at(2)
puts "Массив содержит: " + games.to_s
Rubyrush: Массивы и поток выполнения программы в Ruby
Пример удаления элемента массива по его порядковому номеру.

Как удалить последний элемент в массиве

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

Rubyrush: Массивы и поток выполнения программы в Ruby
Пример удаления трех элементов с конца массива при помощи метода pop.

Командный центр

  • sleep — команда временной паузы в работе программы. sleep 5 — пауза в работе программы на 5 сек.
  • Поток выполнения программы — последовательность выполнения сценария/программы, которая зависит от запрограммированных условий.
  • Клювики — используются для перемещения различных объектов в массив.
  • команда delite — используется для удаления элемента массива по его точному содержанию.
  • команда delite_at — команда удаления элемента массива по его порядковому номеру в массиве.
  • массив — это упорядоченная коллекция произвольных объектов с целочисленной индексацией. А еще можно описать массив как сложный объект, состоящий из упорядоченного перечня других объектов.
  • concat — метод позволяющий объединять массивы по средствам конструкции типа: a.concat(b). при этом

Практические задания

Задание №1: Объединение массивов

В задании нам нужно создать два массива. Первый с мужскими именами, а второй с женскими.

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

При этом нельзя повторять имена в коде программы. Объединять нужно при помощи методов и команд изученных в уроке.

В конце нужно вывести все три массива на экран.

# Мужской имена
mens = ["Денис", "Борис", "Иван", ]
# Женские имена
womens = ["Маша", "Роза", "Диана", ]
# Новый массив
general = mens.to_s + womens.to_s
puts "Мужской массив содержит: " + mens.to_s
puts
puts "Женский массив содержит: " + womens.to_s
puts
puts "Общий массив содержит: " + general.to_s

# Новый массив обеднённый методом concat
general = "#{mens.concat(womens)}"
puts "Общий массив содержит: " + general.to_s

Задание №2: Инвертирование массива

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

Тут мне пригодилось два метода:

  • reverse — позволяет вывести элементы массива в обратном порядке. При этом исходный порядок элементов массива не изменяется.
  • reverse! — полностью меняет меняет сам объект — порядок элементов массива на обратный: начало становится концом, а конец — началом.

Метод с восклицательным знаком обычно в руби меняет сам объект, на котором он вызван (или делает что-то неожиданное или ответственное). А обычный reverse просто вернет массив в обратном порядке, но не изменит исходный объект.

По итогу работы над задачкой у меня получилась вот такая программка:

patrick = [1, 2, 3, 4, 5]
puts "Мой массив " + patrick.to_s

puts "Массив наоборот " + patrick.reverse.to_s

puts "Мой массив всё еще в изначальном состоянии " + patrick.to_s

puts "А теперь я изменил его на обратный порядок" + patrick.reverse!.to_s
puts "Мой массив на выходе" + patrick.to_s
Rubyrush: Массивы и поток выполнения программы в Ruby
Пример работы над изменением порядка элементов в массиве.

Задание №3: Выбор машины из массива

Третья задачка тоже была простой. Мне нужно было составить массив из 10 разных марок автомобилей,

Затем вывести на экран консоли колличество элементов массива.

После чего предложить пользователю назвать номер автомобиля и вернуть ему сообщение о том, какую марку автомобиля он получит от нас в подарок, в зависимости от введённого им номера.

Если пользователь вводит номер, которого в массиве нет — мы сообщаем ему об этом.

Моё решение задачи

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

# Объявите в программе массив из марок автомобилей (всего около 10).
cars = ["Audi", "BMW", "Mazda", "Toyota", "Lexus", "Shkoda", "Kia", "Ford", "Subaru", "Jeep"]
# Выведите на экран размер этого массива (но не сам массив)
puts "Всего у нас представленно: " + cars.length.to_s + " марок автомобилей."
# спросите у пользователя одно число — номер марки автомобиля, который он хочет получить в подарок.
puts "Выберите любой номер от 1 до 10, марку автомобиля, который мы подарим вам сегодня."
choice = gets.chomp
# Выведите ему ту марку автомобиля, номер которой запросил пользователь.
if choice == "1"
  puts "Поздравляем! Вы выиграли: " + "Audi"
elsif
  choice == "2"
  puts "Поздравляем! Вы выиграли: " + "BMW"
elsif
  choice == "3"
  puts "Поздравляем! Вы выиграли: " + "Mazda"
elsif
  choice == "4"
  puts "Поздравляем! Вы выиграли: " + "Toyota"
elsif
  choice == "5"
  puts "Поздравляем! Вы выиграли: " + "Lexus"
elsif
  choice == "6"
  puts "Поздравляем! Вы выиграли: " + "Shkoda"
elsif
  choice == "7"
  puts "Поздравляем! Вы выиграли: " + "Kia"
elsif
  choice == "8"
  puts "Поздравляем! Вы выиграли: " + "Ford"
elsif
  choice == "9"
  puts "Поздравляем! Вы выиграли: " + "Subaru"
elsif
  choice == "10"
  puts "Поздравляем! Вы выиграли: " + "Jeep"
else 
  # А если он запросил номер, которого нет (отрицательное число, ноль или больше, чем есть в массиве) — сообщите ему, что он ошибся.
  puts "Извините, машины с номером #{choice} у нас нет :(",
  "Вы слышали что мы говорили ранее? Номера от 1 до 10!"
end

Их решение задачи

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

Давайте разберемся в их решении и оценим оба варианта

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

Проверка входных данных осуществляется по средствам двух обязательных условий, если хотя бы одно из них не возвращает true — пользователю показывают сообщение с текстом типа: «Ошибка номера. Такой машинки нет у нас.»

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

А вот если бы нам нужно было что бы хотя бы одно значение было верным, мы моги бы использовать оператор ИЛИ ||.

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

Мне не оч нравится такое решение.

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

Так что возьму их решение на вооружение и буду иметь его в виду.

Их решение можно посмотреть ниже:

# # Объявим массив марок автомобилей
cars = [
  'Ford',
  'Mercedes',
  'Maybach',
  'Citroen',
  'Mazda',
  'Toyota',
  'Lexus',
  'Nissan'
]

# Выведем все машины на экран
puts 'У нас всего ' + cars.size.to_s + ' машин. Вам какую?'

# Спросим номер машины и преобразуем его в число методом to_i
# Люди считают с единицы, поэтому вычтем 1 из числа, которое ввел пользователь
number = gets.to_i - 1

# Проверим, что введено число от 0 до размера массива (не включительно)
if number >= 0 && number < cars.size
  # Если условие выполнено, можно показать выбранный элемент
  puts 'Поздравляем, вы получили:'
  puts cars[number]
else
  # Если условие не выполнено, машину из массива достать нельзя
  puts 'Извините, машины с таким номером у нас нет :('
end

Задание №4: Камень — Ножницы — Бумага

Эта задачка была супер! Снова делали игру. В этот раз это классика — «Камень, ножницы, бумага».

Всё как в реальной игре. Правила такие же. В задании говорилось следующее:

Напишите игру «камень — ножницы — бумага». Пользователь вводит свой вариант в консоли и играет против компьютера. И видит результат игры. Компьютер должен выбирать случайный вариант.

https://rubyrush.ru/steps/arrays-thread-04

Вот тут мне как раз пригодился вариант, который ребята демонстрировали на предыдущем задании — про машины. Я про тот ход в котором мы вычитаем единицу, что бы привести все значения к общему стандарту.

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

Вот мой шедевр


# encoding: utf-8
game_items = ["Камень", "Бумага", "Ножницы"]

# Компьютер должен выбирать случайный вариант.
random = rand(game_items.length)
puts "Рандом выпало: " + random.to_s
puts random.class

# Пользователь вводит свой вариант в консоли
puts "Выберите что показать сейчас?"
puts "1. Камень"
puts "2. Бумага"
puts "3. Ножницы"
player_turn = gets.chomp

# приводим к единому стандарту
player_turn = player_turn.to_i - 1

# если игрок показал камень?
if player_turn == 0
  if random == 0
    puts "Ничья!"
  elsif 
    random == 1
    puts "Поражение!"
  else
    random == 2
    puts "Победа!"
  end
# если игрок показал бумагу?  
elsif 
  player_turn == 1
  if random == 0
    puts "Победа!"
  elsif 
    random == 1
    puts "Ничья!"
  else
    random == 2
    puts "Поражение!"
  end
# если игрок показал ножницы?
elsif
  player_turn == 2
  if random == 0
    puts "Поражение!"
  elsif 
    random == 1
    puts "Победа!"
  else
    random == 2
    puts "Ничья!"
  end
else
  # если игрок ничего не ввёл или ввел некорректные данные
  puts "Вы ввели не корректное значение",
  "или просто оставили строку пустой"
end

Подсказки

После того как я написал программу, протестировал её и опубликовал, полез смотреть подсказку и их решение.

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

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

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

Их решение задачи

# encoding: utf-8

# объявим массив человеческих названий позиций
names = ['Камень', 'Ножницы', 'Бумага']
# на нулевой позиции - камень, на 1-й ножницы, на 2-й бумага

# вызов rand(3) дает случайное число от 1 до 3, не включая 3 (см. документацию)
computer_choice = rand(3)

puts 'введите вариант: 0 - камень, 1 - ножницы, 2 - бумага'

# записываем выбор пользователя из консоли, преобразуя в число
user_choice = gets.to_i

# выводим человеческое название варианта выбранного компом
puts 'Компьютер выбрал: ' + names[computer_choice]

# выводим человеческое название варианта выбранного человеком
puts 'Вы выбрали: ' + names[user_choice]

# в наших числах логика игры такая:
# 0 бьет 1, 1 бьет 2, но 2 бьет 0

if user_choice == computer_choice
  puts 'Ничья'
elsif user_choice == 0 && computer_choice == 1 # у вас 0-камень И у компьютера 1-ножницы
  puts 'Победили Вы'
elsif user_choice == 1 && computer_choice == 2 # у вас ножницы И у компа бумага
  puts 'Победили Вы'
elsif user_choice == 2 && computer_choice == 0 # бумага И камень
  puts 'Победили Вы'
else # во ВСЕХ ОСТАЛЬНЫХ случаях победа за компом
  puts 'Победил Компьютер'
end

С какими сложностями я столкнулся при решении этой задачи

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

А потом я подумал:

  1. Программа на винде с кириллическими буквами выдаст ошибку при вводе, так как там кодировка может полететь.
  2. Если я захочу усложнить задачу, добавив еще вару вариантов, например ящерицу и спока, как в сериале «Теория большого взрыва», то это будет сложно, так как в текстовых именованиях в больших массивах можно запутаться.

В итоге решил сделать как в предыдущей задаче делали ребята — авторы курса. И стандартизировал варианты ответов в числа и из вариантов ответов игрока вычитал единицу, что бы соответствовало элементам массива.

Юрий Ронин