Ruby & Rails: веб-разработка с удовольствием

На этот раз буду рассказывать не только про метапрограммирование, но и про Ruby, а также про алгоритмы — сегодня вспомним классику и посмотрим, как она нам явится в Ruby-строках реализации метода qsort. Блог только начинается, про настоящее метапрограммирование пока говорить рано, поэтому позволю себе отступления от основной темы. Предыдущая часть: bang-методыВ языке Ruby некоторые методы имеют два своих варианта — метод отображающий и метот преобразующий. Метод отображающий создаёт новый объект, а метод преобразующий преобразует объект на месте. Имена этим методам дают одинаковые, только к последнему добавляют в конец символ '!' (bang!!).
Аналогичные пары имеем для методов select (отфильтровать элементы по заданному фильтру), map (преобразовать элементы массива согласно заданной функции) и flatten (раскрыть вложенные массивы, чтобы получился одномерный массив, элементы которого не есть массивы). Напишем метод make_nobangВ закрепление материала предыдущего поста, напишем метод определяющий методы. Он будет называться make_nobang. class String def downcase! ... end def upcase! ... end def sub! ... end def strip! ... end make_nobang :downcase, :upcase, :sub, :gsub, :strip end Нужно только реализовать метод make_nobang: class Module def make_nobang(*methods) methods.each do |method| define_method("#{method}") do |*args| self.dup.send("#{method}!", *args) end end end end Проверочный код может быть, например, таким: class String def down! self.downcase! end make_nobang :down end a = "abcABC" puts a.down puts a a.down! puts a К чему я пишу все эти простые, в принципе, вещи? У меня есть по меньшей мере три поинта: a.send('xyz' ,1, 2). Принципиальная разница в том, что первый аргумент в последнем случае может быть вычисляемы выражением. Есть и другое различие — send игнорирует области видимости protected и private. Метод send — это следующий важный метод динамического программирования наряду с уже упомянутыми методами eval, class_eval, instance_eval, instance_variable_get, instance_variable_set, define_method. class Array def m!(&block) self.map!(&block) end make_nobangs :m end a = [1,2,3] puts (a.m{|i| i*i}).inspect puts a.inspect В результате вы получите avoroztsov@subuntu:~/meta-lectures$ ruby -v make_nobang.rb Приведённая реализация make_nobang плоха, поскольку class Module def make_nobangs(*methods) methods.each do |method| define_method("#{method}") do |*args, &block| self.dup.send("#{method}!", *args, &block) end end end end Пункт 3 решается. См. методы private_methods, protected_methods,… для класса Object. Метод make_bangМетоды sort и sort! уже есть у массивов. Но давайте, чтобы этот пост не пропал даром, напишем сами на Ruby быструю сортировку и реализуем методы qsort и qsort! Метод 1Попробуем использовать метод partition, определенный для экземпляров Enumerable: class Array def qsort return self.dup if size < =1 # делить на части будем по первому элементу l,r = partition {|x| x < = self.first} c,l = l.partition {|x| x == self.first} l.qsort + с + r.qsort # конкатенация трех массивов end end Метод 2Удобно делить исходный массив сразу на три массива. Для этого определим метод partition3: class Array # given block should return 0, 1 or 2 # -1 stands for 2 # outputs three arrays def partition3 a = Array.new(3) {|i| []} each do |x| a[yield(x)] < < x end a end def qsort return self.dup if size < =1 c,l,r = partition3 {|x| first < => x} l.qsort + c + r.qsort end end Необходима также версия функции сортировки, которая сортирует массив «на месте». Вот она: class Array def qsort! self.replace(self.qsort) end end a = [1,7,6,5,4,3,2,1] p a.qsort # => [1, 1, 2, 3, 4, 5, 6, 7] p a # => [1,7,6,5,4,3,2,1] a.qsort! p a # => [1, 1, 2, 3, 4, 5, 6, 7] Но тоже самое можно было бы сделать, не пренебрегая метапрограммированием: def make_bang(*methods) methods.each do |method| define_method("#{method}!") do |*args| self.replace(self.send(method, *args)) end end end class Array make_bang :qsort end PS:Надо сказать, что методы make_nobang и make_bang я придумал сам и ничего похожего пока в core и std, видимо, нет и не будет в ближайшее время. :))) PSS: Вопросы на понимание и задачи1. Почему у класса Set нет метода "sort!"? 2. Почему у разных классов (например у Float) нет метода "to_i!"? 3. Почему нет унарного оператора "++"? class Module def make_nobang(*methods) methods.each do |method| bang_method = "#{method}!" define_method("#{method}") do |*args| self.dup.send(bang_method, *args) end end end end и class Module def make_nobang(*methods) methods.each do |method| define_method("#{method}") do |*args| self.dup.send("#{method}!", *args) end end end end Работает ли первый код? Доступна ли локальная переменная bang_method из создаваемого метода? Если доступна, то не чудо ли это? Она же локальная! А создаваемый метод будет вызываться потом, когда метод make_nobang уже закончит свое выполнение! Если всё таки оба способа работают, то какой из них эффективнее? Комментариихорошая статья, но “PSS: Вопросы на понимание и задачи” делают её поуниверситетски занудной Часто используемые элементы Textile, которые могут вам пригодиться при написании текста:
О дополнительных элементах можно прочитать тут: http://redcloth.org/textile
Войдите, чтобы оставить комментарий
|
artem.voroztsov
Был давно
Облако тегов
#rails
amazon web services
array
blocks
bootstrapping
closures
cloud computing
console
container
education
email
emerge
fiber
finders
gentoo
Getting Real
helper
hosting
JavaScript
jquery
lambda
lazy
liquid
metaprogramming
monkey patching
passenger
pipeline
plugin
proc
qsort
rails
rails 3
returning
ror
ruby
sass
serialize
snippets
tricks
tutorials
ubuntu
views
боевые искусства
вакансия
искусство
массив
мысли вслух
работа
Работа
фриланс
|
|||||||||||||||||||||||||||||||||||||||