Статьи

GSwR IV: зацикливание на массивах и хешах

В последнем посте мы рассказали о числах и их методах. В этом посте мы рассмотрим некоторые из коллекций Руби, такие как Arrays, Hashes и Ranges. Мы посмотрим, чем они являются, для чего они используются и что они могут сделать. Мы также узнаем, как перебирать коллекцию и как создавать циклы. В завершение мы создадим программу, которая имитирует перетасовку колоды карт, а затем превратим ее в онлайн-игру «Играй в свои карты прямо» с использованием Sinatra.

Массивы

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

primes = [2, 3, 5, 7, 11]

Вы можете поместить любой тип объекта в массив, например строки:

 fruits = ["apples" "pears" "bananas"]

Вам даже не нужно использовать объекты одного типа:

 mixture = [1, "two", 3.0, "IV" ]

Вы даже можете иметь массив массивов, известный как многомерный массив. Это можно использовать для создания координатной системы, например так:

 coordinates = [[1,3],[4,2]]

Методы массива

Массивы являются мощным инструментом в наборе инструментов Ruby и имеют несколько очень полезных методов. Чтобы продемонстрировать некоторые из них, я буду использовать массив детей Старка:

 starks = %w(Rob Sansa Arya Bran Rickon)
=> ["Rob","Sansa","Arya","Bran","Rickon"]

Это использует немного другой синтаксис для создания массива. Если массив, который вы создаете, состоит из строк из одного слова, то вы можете использовать флаг% w в начале, чтобы указать это, а затем опустить кавычки на строках внутри массива, а также запятые, разделяющие их.

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

 starks[1]
=> "Sansa"

Обратите внимание, что это не первый член в массиве — это потому, что нумерация начинается с нуля. Система нумерации также использует отрицательные значения, чтобы начать отсчет со спины, поэтому starks[-1]

 starks[-1]
=> "Rickon"

Существуют также методы для нахождения первого и последнего элемента в массиве:

 starks[0]
=> "Rob"
starks.first
=> "Rob"
starks[-1]
=> "Rickon"
starks.last
=> "Rickon"

Мы также можем вернуть подмножество массива, указав второй параметр, который указывает длину подмассива:

 starks[2,3]
=> ["Arya","Bran","Rickon"]

В качестве альтернативы, мы можем предоставить объект Range (подробнее о диапазонах позже), который будет производить поднабор из одного индекса в другой:

 starks[2..4]
=> ["Arya","Bran","Rickon"]

Если мы хотим узнать, сколько элементов в массиве, то мы можем использовать метод length

 starks.length
=> 5

Чтобы узнать, содержит ли массив объект с помощью include? метод:

 starks.include?("Arya")
=> true
starks.include?("Jon")
=> false

Чтобы добавить новое значение в конец массива, используйте метод push

 starks.push("Jon")
=> ["Rob","Sansa","Arya","Bran","Rickon","Jon"]

Для этого есть общий сокращенный оператор:

 starks << "Jon"
=> ["Rob","Sansa","Arya","Bran","Rickon","Jon"]

Мы также можем удалить последний объект в массиве, используя метод pop

 nightswatchman = starks.pop
=> "Jon"

Мы можем отсортировать массив в порядке (в алфавитном порядке по умолчанию для объектов String):

 starks.sort
=> ["Arya","Bran","Rickon","Rob","Sansa"]

Это фактически не изменило порядок массива, хотя:

 starks
=> ["Rob","Sansa","Arya","Bran","Rickon"]

Для этого нам нужно использовать метод sortsort! :

 starks.sort!
=> ["Arya","Bran","Rickon","Rob","Sansa"]
starks
=> ["Arya","Bran","Rickon","Rob","Sansa"]

Теперь порядок массива изменился навсегда.

reverse

 starks.reverse
=> ["Sansa", "Rob", "Rickon", "Bran", "Arya"]
starks
=> ["Arya", "Bran", "Rickon", "Rob", "Sansa"]
starks.reverse!
=> ["Sansa", "Rob", "Rickon", "Bran", "Arya"]
starks
=> ["Sansa", "Rob", "Rickon", "Bran", "Arya"]

Мы можем объединить все элементы массива в строку, используя метод join Это принимает аргумент, чтобы указать, что вы хотите использовать в качестве разделителя:

 starks.join(",")
=> "Sansa,Rob,Rickon,Bran,Arya"

Хэш

Хэши — это список пар ключ и значение. Мы можем создать хеш-литерал, поместив его в фигурные скобки

 stark = { :name => "Eddard Stark" }

Обычной практикой является использование символов для клавиш, поскольку они используют память более эффективно . Существует сокращение, которое можно использовать начиная с Ruby 1.9:

 stark = {   name: "Eddard Stark",
            lady: "Catelyn Stark",
            sigil: "Direwolf",
            motto: "Winter is Coming",
            residence: "Winterfell",
            children: ["Rob","Sansa","Arya","Bran","Rickon"]
        }

Чтобы получить доступ к значению в хэше, просто обратитесь к ключу:

 stark[:motto]
=> "Winter is Coming"

Вы можете даже иметь вложенные хэши:

 houses = { :stark => { sigil: "Direwolf", residence: "Winterfell" },
       :lannister => {sigil: "Lion", residence: "Casterly Rock" }
     }

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

 houses[:lanister][:sigil]
=> "Lion"

Изменяется

Диапазон может использоваться для представления последовательности значений. Вы можете создать диапазон, разделив начало и конец диапазона на 2 точки (включительно) или 3 точки (исключая). Например, 1 .. 101 ... 10

Мы также можем создать диапазон букв:

 alphabet = "a".."z"
=> "a".."z"

К сожалению, вы не можете получить доступ к отдельным значениям Range, как вы можете с массивами и хешами:

 alphabet[2]
NoMethodError: undefined method `[]' for "a".."z":Range
  from (irb):26
  from /home/daz/.rvm/rubies/ruby-2.0.0-p247/bin/irb:16:in `<main>'

Но есть удобный метод to_a

 alphabet.to_a
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

Диапазоны чаще всего используются с целыми числами и строками, но на самом деле могут использоваться с любым сопоставимым объектом (т. Е. У него есть способ определения порядка, см. Здесь для получения дополнительной информации).

Другой

Есть и другие коллекции Ruby, одна из которых Set Это то же самое, что и массив, но может содержать только уникальные значения. Фактически, любой класс Ruby можно заставить работать как коллекцию, включив модуль Enumerable

Loops

Цикл — это структура управления, которая будет повторять блок кода, пока не будет выполнено определенное условие. Блок кода начинается с команды doend Вот пример цикла, использующего условие until

 bottles = 10
until bottles == 0 do
  puts "There were #{bottles} beer bottles hanging on the wall ... "
  bottles -= 1
  puts "And if one beer bottle should accidently fall ..."
  puts "There'd be #{bottles} beer bottles hanging on the wall." 
end

Условие здесь — количество бутылок равно нулю. Блок кода между doend Обратите внимание, что значение в переменной bottles Важно, чтобы вы завершили цикл, иначе вы застрянете в бесконечном цикле.

Код выше может быть написан с использованием условия while

 bottles = 10
while bottles > 0 do
  puts "There were #{bottles} beer bottles hanging on the wall ... "
  bottles -= 1
  puts "And if one beer bottle should accidently fall ..."
  puts "There'd be #{bottles} beer bottles hanging on the wall." 
end

Время и while Ruby предлагает их оба, чтобы вы могли написать более выразительный код, который читается больше как английский.

итераторы

Методы итератора позволяют нам циклически проходить по массиву, хешу или диапазону (или даже любому классу, который включает модуль until Наиболее распространенным методом итератора является метод Enumerable Это будет проходить через каждый объект в коллекции и затем запускать блок кода. Например, следующий фрагмент кода выводит первые пять квадратных чисел:

 each

Обратите внимание, что блок принимает параметр, который находится внутри | pipe | и приходит сразу после оператора numbers = [1,2,3,4,5]
numbers.each do |number|
puts number * number
end
do
, Это представляет значение в массиве на каждой стадии цикла. Этот цикл установит |number|number

Другой способ создания блока — использовать фигурные скобки для обозначения начала и финских вместо numberdo Таким образом, приведенный выше код можно записать более кратко, как:

 end

Я также использовал диапазон вместо массива, просто чтобы показать, что numbers = 1 .. 5
numbers.each { |number| puts number * number }

Общее согласие в сообществе Ruby состоит в том, чтобы использовать блок each{ .. }

Выполняя итерацию по хешу, используя do .. end Вот и пример использования абсолютного хэша из ранее:

 each

Это дает следующий вывод:

 stark = {   
    name: "Eddard Stark",
    lady: "Catelyn Stark",
    sigil: "Direwolf",
    motto: "Winter is Coming",
    residence: "Winterfell",
    children: ["Rob","Sansa","Arya","Bran","Rickon"]
}
stark.each {|key,value| puts "#{key}: #{value}" }

Другим полезным методом итератора является метод name: Eddard Stark
lady: Catelyn Stark
sigil: Direwolf
motto: Winter is Coming
residence: Winterfell
children: ["Rob", "Sansa", "Arya", "Bran", "Rickon"]
map
Это заменяет каждое значение в массиве возвращаемым значением блока. Таким образом, мы могли бы создать массив квадратных чисел, используя следующий код:

 collect

Обратите внимание, что значение numbers = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
squares = numbers.map { |number| number * number }
=> [1, 4, 9, 16, 25]
numbersmap
Новый массив с именем [1,2,3,4,5]squares Если вы хотите изменить исходный массив, используйте map метод вместо:

 map!

Теперь значения в numbers = [1,2,3,4,5]
numbers.map! { |number| number * number }
=> [1, 4, 9, 16, 25]

Создайте и перемешайте колоду карт

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

 numbers

Здесь мы создаем пустой массив с именем deck = []
suits = %w[ Hearts Diamonds Clubs Spades ]
values = %w[ Ace 2 3 4 5 6 7 8 9 10 Jack Queen King ]
suits.each do |suit|
values.each do |value|
deck << "#{value} of #{suit}"
end
end
deck.shuffle!
p deck
Затем мы создаем массив всех мастей и массив из 13 карт (от туза до короля). Затем мы перебираем каждую масть, а затем для каждой масти, перебираем каждое значение, создавая строку для названия каждой карты (например, «Пиковый туз»), а затем помещаем эту строку в массив колод, используя сокращенный deck

Если вы запустите следующий код, введя operator.

 ruby cards.rb

Этот массив теперь можно использовать для создания всевозможных замечательных карточных игр. Говоря о которых …

Играйте в свои карты прямо в Интернете

Раньше я любил смотреть игровое шоу « Играй в свои карты», когда я был моложе, поэтому я подумал, что сделаю веб-игру, основанную на этом, используя код для колоды карт, которую мы создали выше.

Создайте файл «play your cards_right.rb» и добавьте следующий код:

 ["6 of Spades", "9 of Diamonds", "6 of Hearts", "4 of Spades", "Queen of Spades", "Queen of Hearts", "Ace of Hearts", "8 of Diamonds", "King of Hearts", "2 of Clubs", "3 of Spades", "8 of Clubs", "4 of Diamonds", "4 of Clubs", "10 of Hearts", "5 of Spades", "2 of Spades", "Queen of Clubs", "5 of Diamonds", "9 of Hearts", "10 of Spades", "6 of Diamonds", "7 of Diamonds", "9 of Spades", "7 of Hearts", "2 of Hearts", "8 of Spades", "10 of Diamonds", "7 of Clubs", "8 of Hearts", "King of Clubs", "Jack of Hearts", "5 of Clubs", "3 of Clubs", "King of Spades", "Ace of Clubs", "Ace of Diamonds", "10 of Clubs", "6 of Clubs", "Jack of Spades", "3 of Hearts", "4 of Hearts", "Jack of Clubs", "3 of Diamonds", "Queen of Diamonds", "King of Diamonds", "Ace of Spades", "7 of Spades", "5 of Hearts", "Jack of Diamonds", "9 of Clubs", "2 of Diamonds"]daz@batfink:~/Dropbox/rubysource/drafts/jones$ ruby cards.rb 
["8 of Diamonds", "5 of Clubs", "6 of Clubs", "2 of Hearts", "4 of Hearts", "8 of Hearts", "8 of Spades", "Jack of Hearts", "King of Diamonds", "Queen of Spades", "Queen of Diamonds", "9 of Spades", "6 of Diamonds", "6 of Spades", "9 of Clubs", "3 of Spades", "Ace of Spades", "9 of Hearts", "10 of Diamonds", "7 of Spades", "King of Clubs", "7 of Hearts", "5 of Spades", "7 of Clubs", "Jack of Diamonds", "3 of Hearts", "4 of Diamonds", "9 of Diamonds", "Ace of Hearts", "8 of Clubs", "2 of Diamonds", "2 of Spades", "3 of Clubs", "Queen of Hearts", "Jack of Spades", "10 of Hearts", "6 of Hearts", "Jack of Clubs", "5 of Hearts", "Ace of Diamonds", "2 of Clubs", "King of Spades", "3 of Diamonds", "4 of Clubs", "5 of Diamonds", "4 of Spades", "10 of Clubs", "Ace of Clubs", "King of Hearts", "10 of Spades", "Queen of Clubs", "7 of Diamonds"]

Это начинается с того, что требуется драгоценный камень sinatra и разрешающие сеансы (они нам понадобятся, чтобы отслеживать карты во время игры).

Далее нам понадобится обработчик маршрута, чтобы начать работу и выполнить настройку. Мы поместим это в корневой URL (‘/’):

 require 'sinatra'
enable :sessions

configure do
  set :deck, []
  suits = %w[ Hearts Diamonds Clubs Spades ]
  values = %w[ Ace 2 3 4 5 6 7 8 9 10 Jack Queen King ]
  suits.each do |suit|
    values.each do |value|
      settings.deck << "#{value} of #{suit}"
    end
  end
end

При этом используется массив get '/' do
session[:deck] = settings.deck.shuffle
session[:guesses] = -1
redirect to('/play')
end
settings.deck
Мы также установили другую переменную сеанса, чтобы отслеживать, сколько предположений сделал игрок. После этого мы перенаправляем на маршрут configure

Давайте настроим обработчик маршрута для этого сейчас:

 /play

Прежде всего, вы можете подумать, что этот обработчик маршрута на самом деле не соответствует get '/:guess' do
card = session[:deck].pop

value = case card[0]
when «J» then 11
when «Q» then 12
when «K» then 13
else card.to_i
end

if (value < session[:value] and params[:guess] == ‘higher’) or (value > session[:value] and params[:guess] == ‘lower’)
«Game Over! The card was the #{ card }. You managed to make #{session[:guesses]} correct guess#{‘es’ unless session[:guesses] == 1}. <a href=’/’>Play Again</a>»
else
session[:value] = value
session[:guesses] += 1
«The card is the #{ card }. Do you think the next card wil be <a href=’/higher’>Higher</a> or <a href=’/lower’>Lower</a>?»
end
end Но это так! Часть маршрута «: думаю» является именованным параметром . Строка, которая идет после /play/ Он используется в игре, чтобы проверить, угадал ли игрок «выше» или «ниже». Любое слово может быть введено в этот маршрут. В этом случае слово «игра» использовалось как пустышка, так как игрок еще не сделал предположения.

Первое, что мы делаем в этом обработчике, это используем метод :guesspop Это эквивалент раздачи карты сверху колоды.

Следующий фрагмент кода вычисляет числовое значение карты. При этом используется оператор cardcase Затем он проверяет, является ли он Джеком, Королевой или Королем, и присваивает значения 11, 12 и 13 соответственно. Для всех остальных карт достаточно использовать метод card[0]

Затем следует большой оператор to_i Во-первых, проверка, чтобы увидеть, является ли значение, хранящееся в хэше сеанса с ключом if Затем, сравнивая это с предположением игрока, которое хранится в хэше :valueparams Значение, сохраняемое в params[:guess] Если игрок угадал неправильно, то отображается сообщение «Игра окончена», сообщающее игроку, что это была за карта и сколько правильных предположений они сделали.

Условие else относится к тому, правильно ли игрок угадывает, или, если точнее, к неверному предположению. Это позволяет коду запускаться, если для session[:card] Первое, что происходит, это то, что значение текущей карты сохраняется в params[:guess]

Запустите сервер, набрав session[:value]адресу http: // localhost: 4567 в своем браузере и ruby play_your_cards_right.rb

Это все люди

Это подводит нас к концу этой части серии «Начало работы с Ruby». Массивы являются очень мощной частью любого инструментария программирования, и не так много программ, в которых бы не было своего рода итератора. Хеши также часто появляются во многих программах на Ruby (например, они часто используются для предоставления опций методам в Rails). Надеюсь, теперь немного понятнее, как хеши сеансов и параметров работают в Синатре.

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

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