[ruby, python] - code block
От: DemAS http://demas.me
Дата: 13.03.09 10:32
Оценка:
Я тут решил познакомиться с Ruby и наткнулся code block's, которые
позволяют реализовать вот такие интересные вещи:

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 можем гарантировать закрытие файла,
не полагаясь на память и внимательность пользователя этого класса.

Похожую вещь можно сделать и в Python с помощью ключевого слова with
(http://expl0rer.wordpress.com/2009/03/04/with_in_python/), но даже в этом
случае мы не застрахованы от того, что пользователь при работе с классом
забудет о использовании слова with.

Наверное, в Python можно попробовать использовать декораторы, но я сходу не
нашел возможности получить из декоратора параметр (файловый дескриптор, в
данном случае). Такая возможность есть или параметры передаются только в
декоратор?

Если бы данную задачу решать на обобщенном ООП языке, то наверное можно
было бы воспользоваться примерно такой конструкцией:

Реализовать базовый класс:

Base(object):
private file;

public work_with_file(file):
pass

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

и заставить пользователя создавать для работы с файлами наследника от
моего, перекрывая метод work_with_file(file):

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


Может есть какие-то более удобные паттерны и приемы, не привязанные к
конкретному языку программирования, достигать такой же цели — гарантировать
освобождение ресурсов?
Posted via RSDN NNTP Server 2.1 beta
Re: [ruby, python] - code block
От: z00n  
Дата: 13.03.09 12:18
Оценка: 5 (1)
Здравствуйте, DemAS, Вы писали:

DAS>Может есть какие-то более удобные паттерны и приемы, не привязанные к

DAS>конкретному языку программирования, достигать такой же цели — гарантировать
DAS>освобождение ресурсов?

Конечно есть — для этого нужны первоклассные функции. Вот реализации на нескольких языках — общее уловить несложно:
— Если в языке нет аналога finally(unwind-protect) — написать его
— передавать то, что нужно сделать сделать с ресурсом в виде анонимной функции

http://scala.sygneca.com/patterns/loan
// Scala - есть готовый finally - все просто
def withResource[A](f : Resource => A) : A = {
    val r = getResource()  // Replace with the code to acquire the resource
    try {
        f(r)
    } finally {
        r.dispose()
    }
}

//Client code becomes very simple:
withResource{ r =>
    // do stuff with r....
}


http://groups.google.fi/group/comp.lang.functional/browse_frm/thread/5385c76d82a78171
// SML
// First of all, a function such as 
  fun finally (thunk, effect) = 
      (((fn x => fn () => x) (thunk ()) 
        handle e => fn () => raise e) o effect) () 
// can be provided to make sure that a given effect will be performed after a 
// given thunk returns - whether normally or by raising an exception. 
// It is customary to provide functions that allocate a resource, pass the 
// resource to a given body function, and take care of deallocating the 
// resource.  For example, one could provide the function 
  fun withInputString string = 
      fn body => 
         let 
            val instream = TextIO.openString string 
         in 
            finally (fn () => body instream, 
                     fn () => TextIO.closeIn instream) 
         end 
// for executing a body with an instream that reads from a string.   
// Another example of such a scoped resource management function could be 
  fun withDirStream path = 
      fn body => 
         let 
            val dirstream = OS.FileSys.openDir path 
         in 
            finally (fn () => body dirstream, 
                     fn () => OS.FileSys.closeDir dirstream) 
         end 
// for executing a body with a directory stream.  As you can see, there is 
// some duplication in structure between the above two functions that could 
// easily be factored out, but I'll ignore that aspect in this article.


Lua: http://www.lua.ru/forum/posts/list/15/89.page#704 — основан на SML варианте
--// "библиотечная" функция 
--//  pcall - это аналог try-catch
local function unwind_protect(thunk,cleanup)  
  local ok, result = pcall(thunk)  
  if cleanup then cleanup() end  
  if not ok then 
    error(res,0)  -- // rethrow exception 
  else 
    return result 
  end 
end  

--// общая функция для работы с открытыми файлами  
local function with_open_file(name,mode)  
  return function(body)  
           local f = assert(io.open(name,mode))  
           return unwind_protect(function()return body(f) end,  
                                 function()return f and f:close() end)  
         end  
end  

--// usage: os-copy --   
function os_copy2(source_path,dest_path)  
  return with_open_file(source_path,"rb") (function(source)  
    return with_open_file(dest_path,"wb") (function(dest)  
      assert(dest:write(assert(source:read("*a"))))  
      return 'copy ok'  
    end)  
  end)  
end


Если бы я знал, как в Питоне передавать анонимные функции как аргументы (lambda?) — я добавил бы пример.
Re[2]: [ruby, python] - code block
От: Gajdalager Украина  
Дата: 13.03.09 12:27
Оценка:
Здравствуйте, z00n, Вы писали:

Z>Здравствуйте, DemAS, Вы писали:


DAS>>Может есть какие-то более удобные паттерны и приемы, не привязанные к

DAS>>конкретному языку программирования, достигать такой же цели — гарантировать
DAS>>освобождение ресурсов?

Скип

А в языках без поддержки ФВП можно использовать GOF-паттерн Command.
<< RSDN@Home 1.2.0 alpha 4 rev. 1128>>
Сейчас играет Тінь Сонця — Гнилгород
Re: [ruby, python] - code block
От: Sinclair Россия http://corp.ingrammicro.com/Solutions/Cloud.aspx
Дата: 13.03.09 12:57
Оценка: 5 (1) +1
Здравствуйте, DemAS, Вы писали:

DAS>Я тут решил познакомиться с Ruby и наткнулся code block's, которые

DAS>позволяют реализовать вот такие интересные вещи:

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

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


DAS>То есть, мы, как создатели класса File можем гарантировать закрытие файла,

DAS>не полагаясь на память и внимательность пользователя этого класса.
Ага. Ну, или, аналогичным образом мы могли бы сделать эту вешчь на дотнете:

var s = f.OpenRead(); // нененене, Дэвид Блейн!
var buf = new byte[1024]; // уйди, демон!
s.Read(buf, 0, buf.Length); // мы же забыли вызвать Dispose!

/// А теперь немного уличной магии:
f.OpenRead(s => { 
        var buf = new byte[1024];
        s.Read(buf, 0, buf.Length);
}); // я сделал вам Dispose(),
// осталось скукожить "обычный" OpenRead!


А вот всё, что понадобилось, чтобы оно компилялось:
public static class FileHelper
{
    public static void OpenRead(this FileInfo fi, Action<FileStream> action)
    {
        using (var s = fi.OpenRead())
            action(s);
    }
}



DAS>Если бы данную задачу решать на обобщенном ООП языке, то наверное можно

DAS>было бы воспользоваться примерно такой конструкцией:

DAS>Может есть какие-то более удобные паттерны и приемы, не привязанные к

DAS>конкретному языку программирования, достигать такой же цели — гарантировать
DAS>освобождение ресурсов?
Вообще говоря — да. Вся идея выражается одним словом: callback.
То есть вместо того, чтобы дать пользователю неконтролируемый доступ к некоторому ресурсу (например, FileStream), мы заставляем его передать нам объект, в который мы дадим ресурс на время.
В моем примере всё построено на делегате Action<FileStream>. В жаве, к примеру, то же самое было бы сделано в виде интерфейса (а прикладной код был бы построен на анонимных классах).
Требовать наследования в данном случае — оверкилл. Попробуй себе представить код, который копирует из файла A в файл B.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
http://rsdn.org/File/5743/rsdnaddict.GIF
Re[2]: [ruby, python] - code block
От: DemAS http://demas.me
Дата: 13.03.09 13:08
Оценка:
Z>Если бы я знал, как в Питоне передавать анонимные функции как аргументы (lambda?) — я добавил бы пример.

Угу. Идею понял:

def withResource(f):
    resource = open('decor1.py')
    try:
        f(resource)
    finally:
        resource.close()
        
def readlines(resource):
    for line in resource:
        print line

withResource(readlines)


Ок. В языках. допускающих использование функций, как параметр другой функции возможно такое решение.
В C++, наверное, можно воспользоваться указателем на функцию.
Re[2]: [ruby, python] - code block
От: thesz Россия http://thesz.livejournal.com
Дата: 13.03.09 13:37
Оценка:
DAS>>Может есть какие-то более удобные паттерны и приемы, не привязанные к
DAS>>конкретному языку программирования, достигать такой же цели — гарантировать
DAS>>освобождение ресурсов?
S>Вообще говоря — да. Вся идея выражается одним словом: callback.

"Скобки".

bracket open close action = do
    h <- open
    action h
    close h


И даже имеет некоторое выражение в самих языках программирования. Собственно, открывая скобку { мы даём программисту возможность что-то делать с контекстом, который мы гарантировано вернём в исходное состояние путём закрывающей скобки }.

Они встречаются и в других местах, где надо контролировать состояние, glBegin/glEnd приходит в голову первым.

Это связывает исключения и goto, кстати.

И вообще, такой подход позволяет тесней увязать языки программирования и библиотеки, получая DS(E)L на выходе.

S>То есть вместо того, чтобы дать пользователю неконтролируемый доступ к некоторому ресурсу (например, FileStream), мы заставляем его передать нам объект, в который мы дадим ресурс на время.

S>В моем примере всё построено на делегате Action<FileStream>. В жаве, к примеру, то же самое было бы сделано в виде интерфейса (а прикладной код был бы построен на анонимных классах).
S>Требовать наследования в данном случае — оверкилл. Попробуй себе представить код, который копирует из файла A в файл B.

withFile name action = bracket (openFile name) closeHandle action

copy a b = withFile a $
    \ah -> withFile b $ \bh -> read ah >>= \text -> write bh text


В скобочной нотации как раз в самый раз.

Да и в с объектами-наследниками "хранителя файла" тоже должно получиться неплохо, хотя и более громоздко.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Re: [ruby, python] - code block
От: D. Mon Великобритания http://thedeemon.livejournal.com
Дата: 13.03.09 15:47
Оценка:
Здравствуйте, DemAS, Вы писали:

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


DAS>То есть, мы, как создатели класса File можем гарантировать закрытие файла,

DAS>не полагаясь на память и внимательность пользователя этого класса.

Гарантировать? А исключения как же?
Впрочем, переписать с использованием begin/rescue не сложно.

DAS>Может есть какие-то более удобные паттерны и приемы, не привязанные к

DAS>конкретному языку программирования, достигать такой же цели — гарантировать
DAS>освобождение ресурсов?

Ага, деструкторы.
Re[2]: [ruby, python] - code block
От: DemAS http://demas.me
Дата: 13.03.09 18:12
Оценка:
> Ага, деструкторы.

В управляемых языках, насколько я понимаю, деструктор вызывается при сборке
объекта GC, и когда он уничтожит наш объект мы гарантировать не можем.

А неуправляемых языков не так много и осталось — C++, и пожалуй все.
Posted via RSDN NNTP Server 2.1 beta
Re[3]: [ruby, python] - code block
От: _nn_ www.nemerleweb.com
Дата: 13.03.09 23:33
Оценка:
Здравствуйте, DemAS, Вы писали:

Z>>Если бы я знал, как в Питоне передавать анонимные функции как аргументы (lambda?) — я добавил бы пример.


DAS>Угу. Идею понял:


DAS>
DAS>def withResource(f):
DAS>    resource = open('decor1.py')
DAS>    try:
DAS>        f(resource)
DAS>    finally:
DAS>        resource.close()
        
DAS>def readlines(resource):
DAS>    for line in resource:
DAS>        print line

DAS>withResource(readlines)
DAS>


DAS>Ок. В языках. допускающих использование функций, как параметр другой функции возможно такое решение.

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

DAS>В C++, наверное, можно воспользоваться указателем на функцию.

В С++ пользуются шаблоннами. Смотреть Standard Library:

template<typename F>
void do(F f)
{
 before();
 try { f(); } catch(...) { }
 after();
}
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: [ruby, python] - code block
От: Sinclair Россия http://corp.ingrammicro.com/Solutions/Cloud.aspx
Дата: 14.03.09 02:55
Оценка:
Здравствуйте, thesz, Вы писали:

T>В скобочной нотации как раз в самый раз.

Это понятно.
T>Да и в с объектами-наследниками "хранителя файла" тоже должно получиться неплохо, хотя и более громоздко.
Вот это — нет. Непонятно, как мы будем наследоваться от хранителя файла дважды.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
http://rsdn.org/File/5743/rsdnaddict.GIF
Re[3]: [ruby, python] - code block
От: Adriano  
Дата: 14.03.09 10:20
Оценка:
Здравствуйте, DemAS, Вы писали:
...
DAS>Ок. В языках. допускающих использование функций, как параметр другой функции возможно такое решение.
DAS>В C++, наверное, можно воспользоваться указателем на функцию.

В С++ есть деструкторы и все ресурсы, которые были инициализированы, будут гарантированно освобождены.

{
  std::ifstream f("1.txt");
  // работаем с файлом
  ...
  //закрытие файла при уничтожении переменной f
}
Re[3]: [ruby, python] - code block
От: Adriano  
Дата: 14.03.09 10:21
Оценка:
Здравствуйте, DemAS, Вы писали:


>> Ага, деструкторы.


DAS>В управляемых языках, насколько я понимаю, деструктор вызывается при сборке

DAS>объекта GC, и когда он уничтожит наш объект мы гарантировать не можем.

а в С++ можем
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.