середа, 7 липня 2010 р.

Нові методи для роботи з масивами у Ruby 1.9.2

Робота з масивами та аналогічними об’єктами у Ruby є дуже простою. З Ruby 1.9.2 ми отримали більше цікавих можливостей :)

У файлі NEWS написано:
* Array
    * new method:
      * Array#keep_if
      * Array#repeated_combination
      * Array#repeated_permutation
      * Array#rotate
      * Array#rotate!
      * Array#select!
      * Array#sort_by!

    * extended methods:
      * Array#{uniq,uniq!,product} can take a block.
...
  * Enumerable
    * New methods:
      * Enumerable#chunk
      * Enumerable#collect_concat
      * Enumerable#each_entry
      * Enumerable#flat_map
      * Enumerable#slice_before

У цій статті коротко розглянемо ці методи.


select! та keep_if

ary.select! {|item| block }
Метод select! вибирає тільки ті елементи масиву ary для яких значення в середині блоку block рівне true. Він є аналогічним новому методу keep_if з однією різницею: select! повертає nil якщо ніяких змін не було зроблено. keep_if завжди повертає об’єкт.
> (1..10).select {|i|  i % 3 == 0 }   #=> [3, 6, 9]
=> [3, 6, 9]

Більше комбінацій і перестановок.
Ruby 1.9 вніс корисні методи поєднання(Array#combination) і перестановки(Array#permutation). Тепер також існують Array#repeated_combination і Array#repeated_permutation:
> [1, 2, 3].combination(2).to_a
=> [[1, 2], [1, 3], [2, 3]]
> [1, 2, 3].repeated_combination(2).to_a
=> [[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]
> [1, 2, 3].permutation(2).to_a
=> [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
> [1, 2, 3].repeated_permutation(2).to_a
=> [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]


rotate / rotate!
Цей метод видаляє перший елемент і додає його до початку. Можна передати ціле число, скільки кроків він повинен "крутити" (можливе і негативне значення).

> [1, 2, 3, 4, 5].rotate
=> [2, 3, 4, 5, 1]
> [1, 2, 3, 4, 5].rotate(2)
=> [3, 4, 5, 1, 2]
> [1, 2, 3, 4, 5].rotate(-2)
=> [4, 5, 1, 2, 3]



Невеликі зміни: sort_by, uniq / uniq!, product
Ці методи тепер можуть приймати блок.
  • sort_by (блок визначає, як повинен бути сортований масив). Крім того доступний новий метод предикат sort_by!
    > %w{ apple pear fig }.sort_by {|word| word.length}
    => ["fig", "pear", "apple"]
    
  • product (повертає масив з усіх комбінації елементів усіх масивів) тепер також приймає блок, який обробляє кожен результат, а не повертає його
    
    > [1,2,3].product([4,5])
    => [[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]
    > [1,2].product([3,4],[5,6])
    => [[1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6]]
    
  • uniq (видаляє дублікати) тепер може приймати блок.
    
    > c = [ "a:def", "a:xyz", "b:abc", "b:xyz", "c:jkl" ]
    > c.uniq
    => ["a:def", "a:xyz", "b:abc", "b:xyz", "c:jkl"]
    > c.uniq{|s| s[/^\w+/]}
    => ["a:def", "b:abc", "c:jkl"]
    
    

flat_map та його аліас collect_concat
Цей метод працює як map, але якщо якийсь елемент також є Enumerable, блок виконується і для кожного з його дочірніх елементів (але не рекурсивно).
> [[1,2],[3,[4,5]]].flat_map{|i|i}
=> [1, 2, 3, [4, 5]]

each_entry
Аналогічний методу each, але може задовольняти кілька аргументів, як єдиний масив. Особисто я не зрозумів суті цього методу.

chunk(англ. - шматок)
Ще один дуже цікавий , але трохи дивний у використанні метод. Він розбиває себе на кілька Enumerator-ів (шматків), використовуючи правило, задане у блоці. Воно тримає разом ті частини, що "відповідають" в серіях. На виході маємо результат фільтру і Enumerator послідовних елементів. Для ясності поглянемо на ці приклади.


> (1..42).chunk{|n| n%11 == 0}.each{|result, elements|
   puts "#{result}: #{elements*', '}"
  }

false: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
true: 11
false: 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
true: 22
false: 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
true: 33
false: 34, 35, 36, 37, 38, 39, 40, 41, 42
=> nil

> a=(0..4).map{[]}
=> [[], [], [], [], []]
> (1..42).chunk{|n| n%5}.each{|remainder, elements| a[remainder] += elements}
=> nil
> puts a
[[5, 10, 15, 20, 25, 30, 35, 40], [1, 6, 11, 16, 21, 26, 31, 36, 41], [2, 7, 12, 17, 22, 27, 32, 37, 42], [3, 8, 13, 18, 23, 28, 33, 38], [4, 9, 14, 19, 24, 29, 34, 39]]

Блок chunk також може повертати спеціальні значення: символи, які починаються з підкреслення. В даний час підтримуються два спеціальних символа:
  • :_separator (або nil) - елемент відкинуто
  • :_alone - елемент завжди отримує один шматок(chunk)

slice_before
Цей метод також дозволяє розділити Enumerable. Необхідно вказати шаблон / блок, який повинен відповідати / бути правдою, і це розділить до цього елементу:
> %w|Ruby is 2 parts Perl, 1 part Python, and 1 part Smalltalk|.slice_before(/\d/).to_a
=> [["Ruby", "is"], ["2", "parts", "Perl,"], ["1", "part", "Python,", "and"], ["1", "part", "Smalltalk"]]

Цікаво спостерігати, в якому напрямку рухається Ruby з цими методам. Деякі з них, це всього лиш раніше зниклі (наприклад, select!). Тим не менш, інші, як на мене, мають обмежене коло використання (chunk). Це лише моя особиста думка. Можливо я і помиляюся ;)

Це запис був підготовлений по мотивах http://rbjl.net/27-new-array-and-enumerable-methods-in-ruby-1-9-2-keep_if-chunk

Немає коментарів: