Тестовое задание для стажеров
- Есть массив [1, 2, 12, 34, 35, 6, 0, 34, 122, 124, 789, 999, 33, 54, 763, 893] a) напишите функцию, которая получает на вход исходный массив и возвращает 2 максимальных значения b) напишите функцию, которая получает на вход исходный массив и возвращает 2 минимальных значения
Первое, второе и третье задания в одном файле: tasks_1_2_3.rb
Использование простых библиотечных методов:
arr1 = [1, 2, 12, 34, 35, 6, 0, 34, 122, 124, 789, 999, 33, 54, 763, 893]
arr1.max(2)
arr1.min(2)
Если попробовать написать такую функцию самому, то у меня получилось такое для варианта возвращения двух максимальных значений:
def max_two(arr)
m1 = arr[0]
m2 = arr[1]
m2, m1 = m1, m2 if m2 > m1
arr[2..].each do |tmp|
if tmp > m1
m2 = m1
m1 = tmp
elsif tmp > m2
m2 = tmp
end
end
[m1, m2]
end
Несложно заметить, что функция, возвращающая два минимальных значения, будет отличаться от max_two только несколько раз повторяющимся знаком неравенства. Поэтому я написал функцию высшего порядка, принимающей вторым агрументом функцию сравнения (Proc lambda). Т.о. order_two мы можем использовать для сравнения по нужному критерию (например, сравнивая как строки и т.п.) с возвращением двух значений.
def order_two(arr, f)
m1 = arr[0]
m2 = arr[1]
return [m1, nil] if m2.nil?
m2, m1 = m1, m2 if f.call(m2, m1)
arr[2..].each do |tmp|
if f.call(tmp, m1)
m2 = m1
m1 = tmp
elsif f.call(tmp, m2)
m2 = tmp
end
end
[m1, m2]
end
Тогда искомые функции:
def min_2(arr)
collation = ->(x, y) { x < y }
order_two(arr, collation)
end
def max_2(arr)
collation = ->(x, y) { x > y }
order_two(arr, collation)
end
- Есть массив arr = [{a: 1, b: 2, c: 45}, {d: 123, c: 12}, {e: 87}] a) напишите выражение, которое получает массив всех ключей b) напишите выражение, которое получает массив всех значений с) напишите выражение, которое получает сумму всех значений
Простой вариант с использованием библиотечных функций:
arr2 = [{ a: 1, b: 2, c: 45 }, { d: 123, c: 12 }, { e: 87 }]
values_arr2 = arr2.flat_map { |hash| hash.values }
keys_arr2 = arr2.flat_map { |hash| hash.keys }
summ_values = values_arr2.sum
Алгоритмических вариантов, принципиально отличающихся от простых, я не нашёл, не уходя совсем в дебри. Вот достаточно "далёкий" вариант для a и b:
def vse(position, arr_input)
arr = arr_input.clone
arr.each_with_object([]) do |el,obj|
until el.empty? do
obj.push( el.shift[position] )
end
end
end
def all_keys (arr)
vse(0, arr)
end
def all_values (arr)
vse(1,arr)
end
- Найдите вхождения каждого элемента в массив [ nil, 2, :foo, “bar”, “foo”, “apple”, “orange”, :orange, 45, nil, :foo, :bar, 25, 45, :apple, “bar”, nil] чтобы на выходе получился Hash по типу { элемент => количество вхождений в массив}
В версиях Ruby до 2.6 включительно:
arr3 = [nil, 2, :foo, 'bar', 'foo', 'apple', 'orange', :orange, 45, nil, :foo, :bar, 25, 45, :apple, 'bar', nil]
arr3.each_with_object( Hash.new(0) ) { |el, obj| obj[el] += 1 }
Начиная с Ruby 2.7 появился метод tally с той же функциональностью:
arr3.tally
если же изобретать велосипед, то можно так:
acc = Hash.new(0)
arr_raid = arr3.clone
until arr_raid.empty? do
acc[ arr_raid.pop ] += 1
end
- Напишите функцию a) которая переводит градусы по Цельсию в градусы по Фаренгейту (формулу нужно найти в интернете) b) напишите консольную программу, которая просит юзера ввести число (градусы по Цельсию) и переводит его в Фаренгейты с) необязательно, но будет плюсом Напишите обработку ошибок, если юзер ввел неправильные данные (программа должна просить ввести число заново и сообщать об ошибке, но не прерываться)
Сначала я написал такой работающий вариант:
def cels_to_fahrenheit(value) # Функция задания а
(9 * value) / 5.0 + 32.0
end
def gets_celsium
puts 'Пожалуйста, введите градусы по Цельсию'
gets.chomp
end
loop do
str = gets_celsium
unless str.match?(/^-*\d+/)
puts 'Ошибка, надо вводить только числа без букв, отрицательные - допускаются'
next
end
c = str.to_f
if c < -273.15
puts 'Температура не может быть ниже абсолютного нуля!'
next
end
puts "#{c} градусов по Цельсию будет равно #{cels_to_fahrenheit(c)} градусов по Фаренгейту"
break
end
При вводе допускается если пользователь введет что-то вроде: 20 C.
Регулярным выражением /^-*\d+/ проверяется, чтобы начало введенного выражения было числовым. Сначала минус я хотел ограничить одним знаком или отсутсвием с помощью "?" в регулярном выражении, но потом решил, что то — не нужное ограничение. Но меня первый вариант как то не устраивал. Поэтому изменил код, возможно перемудрив:
class Temperature
def cels_to_fahrenheit(value)
(9.0 * value) / 5.0 + 32.0
end
def start
str = gets_celsium
matches(str) ? (c = str.to_f) : raise
check_existence(c) ? pretty_p(c, cels_to_fahrenheit(c)) : raise
rescue
retry
end
private
def gets_celsium
puts 'Пожалуйста, введите градусы по Цельсию'
gets.chomp
end
def matches(s)
if s.match?(/^-*\d+/)
true
else
puts 'Ошибка, надо вводить только числа без букв, отрицательные - допускаются'
false
end
end
def check_existence(s)
if s < -273.15
puts 'Температура не может быть ниже абсолютного нуля!'
false
else
true
end
end
def pretty_p(cels, fahr)
puts "#{cels} градусов по Цельсию будет равно #{fahr.round(2)} градусов по Фаренгейту"
end
end
Temperature.new.start
Файл с 4 заданием: task_4_Celsium.rb
Отмечу, что неясна необходимая точность и будет ли температура переводиться туда-сюда в задании 4а. В редких случаях может иметь смысл использовать BigDecimal из Std-lib вместо float, если возможны проблемы в чём-то схожие с конвертацией денег. Либо наоборот Integer — если перевод температуры, например, носит информационный характер и используется только для показа на сайте погоды для пользователей, интересующихся, как им одеться.
- Напишите функцию, которая имитирует работу светофора a) на вход она получает один из цветов в виде строки (‘red’, ‘green’, ‘yellow’ ), на выходе будет результат (идти, стоять или ждать) b) напишите это в виде консольной программы, которая не прекращает работу после однократного вызова, а ждет следующих запросов c) необязательно, но будет плюсом напишите обработку некорректных данных и добавьте возможность юзеру завершить работу программы
Простой вариант, выход осуществляется через ввод q при ожидани ввода:
COMMANDS = { 'red' => 'стоять', 'green' => 'идти', 'yellow' => 'ждать' }
# Функция задания а (вместе с константой COMMANDЫ)
def gets_action(input)
COMMANDS[input]
end
loop do
puts 'Ввведите цвет сигнала светофора. Для выхода введите q'
choice = gets.chomp.downcase
break if choice == 'q'
action = gets_action(choice)
if action.nil?
puts 'Некорректные данные, повторите ввод.'
puts "На ваш выбор следующие допустимые цвета: #{COMMANDS.keys.join(', ')}, а для выхода: 'q'"
next
end
puts "На выбранный цвет сигнала светофора следует #{action}"
end
До этого чуть-чуть играл с гемом tty-prompt. Переписал в такой вариант, где пользователь выбирает из списка вариант, а после выбора необходимое действие (стоять, идти, ждать) окрашено соответствующим ему цветом:
Файлы с пятым заданием: task_5_traffic_light_promt.rb, task_5_traffic_light.rb
require 'tty-prompt'
CMD = { 'red' => 'стоять', 'green' => 'идти', 'yellow' => 'ждать' }
def gets_color(input)
CMD[input]
end
def start_light
prompt = TTY::Prompt.new
loop do
choice = prompt.select('Select color or quit: ', CMD.keys + ['quit'] , filter: true)
break if choice == 'quit'
result = gets_color(choice)
prompt.say(result, color: choice.to_sym)
end
end
start_light
- Обязательное задание. Есть таблица students с колонками id int name varchar created_at datetime parent_id int a) посчитайте количество всех студентов b) посчитайте количество студентов с именем Иван c) посчитайте количество студентов созданных после 1 сентября 2020 года
select COUNT(*) from students;
select COUNT(*) from students where name = 'Иван';
select COUNT(*) from students where created_at >= '2020-09-02 00:00:00';
Student.count
Student.where(name: "Иван").count
Student.where(created_at Student.where('created_at >= ?', '2020-09-02 00:00:00').count
- Необязательное задание, но его выполнение будет плюсом. Так же есть таблица parents (см задание 6) id int name varchar created_at datetime a) посчитайте количество студентов с родителями b) посчитайте количество студентов с родителями при том что имя родителя Марина c) посчитайте количество студентов без родителя
select COUNT(*) from students where students.parent_id is NOT null;
select COUNT(*) from students inner join parents
on students.parent_id = parents.id where parents.name = 'Марина';
select COUNT(*) from students where students.parent_id is null;
Student.where.not(parent_id: nil).count
Student.join(:parents).where( parents: { name: 'Марина'} ).count
Student.where(parent_id: nil).count
Файл с запросами на SQL: task_dr6_7_query.sql
- Необязательная, но выполнение будет очень большим плюсом a)Напишите простой блог на рельсе с минимальным функционалом (один автор, который выкладывает посты. Комментарии, сортировки, фильтры и основные рюшечки не обязательны, но остаются на ваше усмотрение и желание. Как и стилизация) b) выложить проект на Heroku
Сначала я думал о том, чтобы «прикрутить» gem devise. А потом понял, что требуется чуть ли не простой вариант из вводного официального руководства. Так как требовалось с минимальным функционалом без обязательной аутентификации/авторизации, то сделал простой вариант (если будет время до проверки - еще поразвиваю его). Блог оформлен отдельным гитхаб репозиторием и задеплоен на Heroku. Рюшешчки прикручивать пока не стал, ограничившись валидацией и небольшим количеством тестов с CI/CD.