Архив

Archive for the ‘Ruby’ Category

Замыкания в Ruby

Функциональные приемы программирования все больше и больше проникают в популярные языки. Так в Scheme я могу создать замыкание – процедуру, которая ссылается на свободные переменные в своем лексическом контексте. В записи это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.

(define (make-adder n)
  (lambda (x) (+ x n)))

(define adder-100 (make-adder 100))
(adder-100 1)

101

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

Замыкания можно использовать и в Ruby:

def make_adder n
  lambda { |x|
    x + n
  }
end

t = make_adder 100
puts t.call 1

Начиная с версии 1.9 будет работать и такая запись:

def make_adder_1_9 n
  ->(x) {
    x + n
  }
end

t_1_9 = make_adder_1_9 100
puts t_1_9.call 1
Рубрики:Ruby, Scheme Метки: ,

Conditional chaining в Ruby

Недавно работал с кодом примерно такого вида:

if a && b && c
   my_object.some_method_because_of_a
   my_object.some_method_because_of_b
   my_object.some_method_because_of_c
elsif a && b && !c
   my_object.some_method_because_of_a
   my_object..some_method_because_of_b
elsif a && !b && c
   my_object.some_method_because_of_a
   my_object..some_method_because_of_c

Conditional chaining в Ruby в явном виде я не нашел, а добавлять

 return self;

мне не хотелось. Поэтому наиболее удачные вариант, который я нашел:

class Some
  def method_a
    puts "a"
  end

  def method_b
    puts "b"
  end

  def method_c
    puts "c"
  end
end

some = Some.new

a = true
b = true
c = true

l = []
l << :method_a if a
l << :method_b if b
l << :method_c if c

l.inject(some) { |obj, method|
  obj.send(method)
  obj
}

Кстати, последний блок кода можно написать так:

l.inject(some) do |obj,method|
  obj.tap{|o| o.send(method)}
end
Рубрики:Ruby Метки:

Клиент Twitter, написанный на Ruby и Gtk

Ноябрь 15, 2009 expl0rer 4 comments

twiВ рамках изучения Ruby написал небольшой twitter клиент с использованием библиотеки GTK.

Возможности пока минимальные:

  • просмотр ленты сообщений друзей
  • просмотр прямых сообщений
  • публикация своих записей

В дальнейшем скорее всего будет дорабатываться.

Рубрики:Linux, Ruby Метки: ,

Статическая vs динамическая типизация

Собственно здесь нет ничего сложного: при статической типизации (static binding) (ее еще называют ранним связыванием – early binding) все типы известны на момент объявления переменной и в дальнейшем не могут изменяться. При динамической типизации (dynamic binding) (оно же позднее связывание – late binding) тип переменной определяется в момент ее инициализации.

А вот на что стоит обратить внимание при обсуждении системы типов языков программирования, так это на то, что разделение на строгую(strongly typing) и слабую(weakly typing) типизацию, в общем то, ортогонально делению на статическую/динамическую типизацию.

Так языки со слабой типизацией позволяют нам вытворять вот такие вещи:

a  = 9
b = "9"
c = concatenate(a, b)  // produces "99"
d = add(a, b)          // produces 18 

В то время как языки со строгой типизацией требуют явного приведения типов:

a  = 9
b = "9"
c = concatenate(  str(a),  b)
d = add(a,  int(b)  )

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

Рубрики:Python, Ruby Метки: ,

Гарантированное освобождение ресурсов

Решил обзорно познакомиться с Ruby как с одним из основных конкурентов Python и наткнулся на вот такую интересную конструкцию:

class File
   def File.open_and_process(*args)
     f = File.open(*args)
     yield f
     f.close()
   end
end

File.open_and_process("file", "r") do |file|
  while line = file.gets
   puts line
  end
end

Создатели класса File практически (исключения я в данной ситуации не рассматриваю) гарантированно освобождают захваченные ресурсы, позволив пользователю класса не задумывать об этом. Или, что еще важнее, освобождение ресурса никак не зависит от памяти и внимательности пользователя класса.

Но это Ruby, а как в других языках программирования подходят к решению данной задачи?

  • С неуправляемыми ООП языками (С++) все понятно – там есть деструкторы, в которых можно освободить все захваченные ресурсы.
  • В неком абстрактном ООП языке программирования можно использовать следующий подход. Реализовать базовый класс:
Base(object):
   private file;

   public work_with_file(file):
     pass

   public run():
     file = openfile()
     work_with_file(file)
     close_file(file)

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

First(Base):
   public work_with_file(file):
   // do some work with file

Но, требовать наследования в данном случае мне кажется чрезмерным и нецелесообразным.

  • В Python можно использовать ключевое слово with, но в этом случае мы не застрахованы от того, что пользователь нашего класса однажды не использует его без with.
  • Пожалуй самым удачным и универсальным решением является передача функции (callback), осуществляющую работу с ресурсом в класс, ответственный за захват и освобождение ресурса. На Python это будет выглядеть так:
def withResource(f):
    resource = open('decor1.py')
    try:
        f(resource)
    finally:
        resource.close()

def readlines(resource):
    for line in resource:
        print line

withResource(readlines)

В С++ в данном случае можно воспользоваться указателем на функцию, как параметром метода класса, работающего с ресурсом.

Обсуждение на RSDN c примерами кода на Haskell, Scala, Lua, SML.

Рубрики:Python, Ruby Метки: , ,