Здравствуйте, c-smile, Вы писали:
CS>Ну как сказать... использовать coroutine для перебора элементов некоего stream это себя не любить.
Здесь начинается ваше заблуждение.
CS>corouitine это отдельный stack. Имплементация оных (corroutines) и в Lua и в Python (generators/yield) выполнена как т.н. называемые "green" thread (a.k.a. cooperative threads, a.k.a. fibers). CS>В принципе green thread значительно легче чем OS thread но тем не менее это не самая простая операция.
Еrlang, Lua, Stackless Python, Limbo и.т.д используют VM threads — они очень дешевые ~600-1000 байт на поток, а скорость переключения контекста соизмерима со скоростью вызова функции. Хорошая реализация продолжений (как в Chez) будет не хуже.
Т.е. для луа цена вопроса: создание потока — ~300 ns, переключение ~100ns + 1K на стек (ns — это 10e-9 sec). Stackless по моим замерам аналогичен по скорости. Limbo в пару раз быстрее, но он статически типизирован.
По поводу структурирования программ посредством микропотоков и передачи сообщений — есть интересная лекция того самого Роба Пайка. Сейчас он работает в Google — разрабатывает для них секретный язык Sawzall. Речь там идет о предтече Limbo, языке Newsqueak.
Здравствуйте, Roman Odaisky, Вы писали:
RO>В модуле ftplib есть такая функция: RO>... RO>Как можно передать ей такой callback, чтобы написать RO>т. е., заставить результат работать как генератор?
afaik, в обычном Питоне это невозможно. Это возможно в Scheme(см. continuations), Lua (см. coroutines), Stackless Python (см. tasklets + channels).
Здравствуйте, c-smile, Вы писали:
CS>Память и количество опреаций на переключение это не единственные ограничения. CS>Есть еще косвенная доп. нагрузка на GC например.
Z>>Т.е. для луа цена вопроса: создание потока — ~300 ns, переключение ~100ns + 1K на стек (ns — это 10e-9 sec). Stackless по моим замерам аналогичен по скорости. Limbo в пару раз быстрее, но он статически типизирован.
CS>Времена в абсолютном значении мало что говорят. Нужны относительные цифири.
CS>Переключение кстати может быть вообще нулевым — вернее равным вызову функции в C. CS>coroutine (или generator в терминах Python) должна получить stack некоего размера в начале исполнения — 1k это хорошо, но CS>может потребоваться dynamic resize оного в runtime. Тоже что-то стоит.
Можно потестировать что-то конктретное.
Z>>По поводу структурирования программ посредством микропотоков и передачи сообщений — есть интересная лекция того самого Роба Пайка. CS>Это действительно интересно. Странно, как-то раньше это прошло мимо меня. Спасибо. CS>Но кстати как я понимаю эффективной имплементации этого дела так и нет.
Это встроенные системные фичи обеих post-UNIX операционных систем от Bell Labs — Plan 9 и Inferno. В Plan 9 это библиотека libthreads (ее можно покрутить под линуксом в составе plan9port). На ней, в частности написан оконный менеджер.
Inferno — это OS постоенная поверх виртуальной машины Limbo — работает на голом железе или поверх эмулятора на полудюжине архитектур. Я ее в режиме эмуляции гонял под XP — у нее jit и работает очень шустро.
исходники: http://code.google.com/p/inferno-os/
урезанная Inferno c интерфейсом из Acme: http://code.google.com/p/acme-sac/
бинарники: http://www.vitanuova.com/inferno/net_download4T.html
русские фанаты http://habrahabr.ru/blogs/os_inferno/
CS>Интерпретация UI модели конечно интересная, но в реалии требуется concurrent multitasking что в общем-то так на так выходит.
Ну, в вышепреведенном списке языков потоки кооперативные только у Lua, у Stackless можно и так и этак, у Limbo и Erlang — concurrent. На скорость это практически не влияет.
Здравствуйте, SergH, Вы писали:
SH>Это всё-таки не вполне полноценные корутины, насколько я понимаю. Питоновская корутина должна возвращать итератор, т.е. объект с предопределённым интерфейсом, у него потом можно вызывать функцию next, а в конце он выбросит исключение. Для возвращаемого значения тут места нет.
Неполноценность питоновских генераторов в другом — ограниченность контекста одной процедурой, тем хорошо раскрыта в Lua wiki.
SH>А yield это просто синтаксический сахар для всего этого.
С вашего позволения придерусь к терминологии, т.к. тут на РСДН принятно слишком вольно обращаться с термином "синтаксический сахар". О синтаксическом сахаре можно говорить в том случае, когда есть тривиальное синтаксическое преобразование, превращающее программу с сахаром в программу без сахара. Например, for-loop тривиально преобразуется в while-loop (используя __iter__, next и StopIteration). yield — примитив языка.
Здравствуйте, Roman Odaisky, Вы писали:
FR>>Так начиная с 2.5 yield в обе стороны работает http://www.python.org/dev/peps/pep-0342/
RO>Так это поможет всё-таки переделать интерфейс на коллбэках в интерфейс на генераторах?
Нет, это всего лишь позволяет организовать двусторонний обмен сообщениями между генератором и вызывающим кодом.
retrlines(self, cmd, callback=None) unbound ftplib.FTP method
Retrieve data in line mode. A new port is created for you.
Args:
cmd: A RETR, LIST, NLST, or MLSD command.
callback: An optional single parameter callable that is called
for each line with the trailing CRLF stripped.
[default: print_line()]
Returns:
The response code.
Как можно передать ей такой callback, чтобы написать
def ftplines(ftp, filename):
. . .
callback = . . .
ftp.retrlines("RETR " + filename, callback)
for l in ftplines(ftp, "/etc/shadow"):
. . .
т. е., заставить результат работать как генератор?
14.07.09 17:19: Перенесено из 'Декларативное программирование'
Здравствуйте, SergH, Вы писали:
SH>Компилятор не поймёт и не оценит. Куда ему девать возвращаемое ftplines значение?
Принципиальной проблемы — куда девать — нет. У корутины может быть множество промежуточных результатов и один конечный. Проблема только в ограниченной семантике корутин в Питоне.
Здравствуйте, palm mute, Вы писали:
PM>Принципиальной проблемы — куда девать — нет. У корутины может быть множество промежуточных результатов и один конечный. Проблема только в ограниченной семантике корутин в Питоне.
Принципиальной нет, но есть синтаксическая.
Это всё-таки не вполне полноценные корутины, насколько я понимаю. Питоновская корутина должна возвращать итератор, т.е. объект с предопределённым интерфейсом, у него потом можно вызывать функцию next, а в конце он выбросит исключение. Для возвращаемого значения тут места нет.
А yield это просто синтаксический сахар для всего этого.
Здравствуйте, Roman Odaisky, Вы писали: RO>т. е., заставить результат работать как генератор?
А как в питоне с многопоточностью? Можно было бы вынести retrlines с коллбеком в один поток (он бы блокировался на ipc), а в другом сделать генератор (который бы получал от первого потока строку и разблокировал бы колллбек).
Здравствуйте, Mr.Cat, Вы писали:
MC>А как в питоне с многопоточностью? Можно было бы вынести retrlines с коллбеком в один поток (он бы блокировался на ipc), а в другом сделать генератор (который бы получал от первого потока строку и разблокировал бы колллбек).
Здравствуйте, SergH, Вы писали:
SH>Это всё-таки не вполне полноценные корутины, насколько я понимаю. Питоновская корутина должна возвращать итератор, т.е. объект с предопределённым интерфейсом, у него потом можно вызывать функцию next, а в конце он выбросит исключение. Для возвращаемого значения тут места нет.
Здравствуйте, FR, Вы писали:
SH>>Это всё-таки не вполне полноценные корутины, насколько я понимаю. Питоновская корутина должна возвращать итератор, т.е. объект с предопределённым интерфейсом, у него потом можно вызывать функцию next, а в конце он выбросит исключение. Для возвращаемого значения тут места нет.
FR>Так начиная с 2.5 yield в обе стороны работает http://www.python.org/dev/peps/pep-0342/
Так это поможет всё-таки переделать интерфейс на коллбэках в интерфейс на генераторах?
Здравствуйте, palm mute, Вы писали:
PM>Неполноценность питоновских генераторов в другом — ограниченность контекста одной процедурой, тем хорошо раскрыта в Lua wiki.
Насколько я понял, это о том же.
SH>>А yield это просто синтаксический сахар для всего этого. PM>С вашего позволения придерусь к терминологии, т.к. тут на РСДН принятно слишком вольно обращаться с термином "синтаксический сахар". О синтаксическом сахаре можно говорить в том случае, когда есть тривиальное синтаксическое преобразование, превращающее программу с сахаром в программу без сахара. Например, for-loop тривиально преобразуется в while-loop (используя __iter__, next и StopIteration). yield — примитив языка.
Согласен, неаккуратно выразился.
Я имел ввиду, что снаружи функция, которая использует yield неотличима от функции, возвращающей итератор. Т.е. если исключить то, как генерируется последовательность, ничего нового не вводится. Компилятор обнаруживает в теле функции слово "yield" и всё понимает сам. Ну, фактически, он оборачивает функцию в корутину, но делает это незаметно, "корутина" это деталь реализации, а не понятие, которое может использовать программист.
В Lua же вытащили её наружу. В результате функцию нужно явно завернуть в корутину (если я прввильно понял текст, Lua я не знаю). И поведение корутины ни с чем другими не спутаешь.
Т.е. в Питоне более удобно решен частный случай проблемы.
Здравствуйте, FR, Вы писали:
FR>Здравствуйте, Mr.Cat, Вы писали:
MC>>А как в питоне с многопоточностью? Можно было бы вынести retrlines с коллбеком в один поток (он бы блокировался на ipc), а в другом сделать генератор (который бы получал от первого потока строку и разблокировал бы колллбек).
FR>Можно, но бесполезно из-за GIL.
Для данной задачи GIL пофиг. Всё равно он хотел исполнять это в одной нитке, так что две которые аккуратно переключаются — будут исполнять то же самое с минимумом оверхеда.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, neFormal, Вы писали:
CS>Сугубо для сравнения вот функция в TIScript которая итерирует все строки в файле path удовлетворяющих template
Оно примерно также и будет в питоне тоже, вопрос был в том чтобы итерировать класс который для этого не предназначен и превратить принимаемые им callback в итератор.
Здравствуйте, FR, Вы писали:
FR>Оно примерно также и будет в питоне тоже, вопрос был в том чтобы итерировать класс который для этого не предназначен и превратить принимаемые им callback в итератор.
А... ну тогда как-то так:
function linesOfResponse( cmd )
{
var lines = [];
function receiver(line) { lines.push(line); }
retrlines(cmd, receiver);
return function() { while(lines.length) return lines.shift(); }
}
for( var line in linesOfResponse( "LIST" ) )
stdout.printf("%s\n", line);
Здравствуйте, FR, Вы писали:
CS>>Сугубо для сравнения вот функция в TIScript которая итерирует все строки в файле path удовлетворяющих template
FR>Оно примерно также и будет в питоне тоже, вопрос был в том чтобы итерировать класс который для этого не предназначен и превратить принимаемые им callback в итератор.
Вот на луа с корутинами, примерно как palm mute выше писал:
-- (* lines-like.lua *)local co = coroutine
-- (* linesLike :: str, str, callback -> thunk *)local functionlinesLike(fname,pat,callback)
return function()
for line in io.lines(fname) do
if line:match(pat) then callback(line) end
end
end
end
-- (* используем *)for line in co.wrap(linesLike('lines-like.lua','^loc',co.yield)) do
print('!!',line)
end
!! local co = coroutine
!! local function linesLike(fname,pat,callback)
Здравствуйте, palm mute, Вы писали:
PM>Здравствуйте, c-smile, Вы писали:
CS>>Или вообще практически в одну строку: CS>>
CS>>function linesOfResponse( cmd ) { var lines = [];
CS>> retrlines(cmd, :line: lines.push(line) );
CS>> return lines; }
CS>>for( var line in linesOfResponse( "LIST" ) )
CS>> stdout.printf("%s\n", line);
CS>>
PM>Буферизовать и отдавать по одной строке не проблема. Мне почему-то кажется, что автору темы не хотелось качать с ФТП-сервера больше, чем нужно.
Ну во первых retrlines() по своей спецификации и так читает всё. Т.е. callback будет вызван для каждой
line. А во вторых если уже говорить про суть дела то я не вижу особой разницы в написании скажем так:
retrlines("LIST", :line:{ .... = line;... });
и так
for( var line in responselines("LIST")) { .... = line;... }
Разница в основном сугубо синтаксическая.
А кстати интересно бы придумать синтаксис котрый бы позволил "разворачивать" такие вызовы....
Как-то так что-ли:
for(var line in retrlines("LIST",@))
{ ... = line;... }
Здравствуйте, c-smile, Вы писали: CS>Разница в основном сугубо синтаксическая.
Т.е. ты предлагаешь прочитать все строки в список, а потом по нему пройтись? Это очевидное решение, но оно не подходит, если строк очень много.
Здравствуйте, Mr.Cat, Вы писали:
MC>Здравствуйте, c-smile, Вы писали: CS>>Разница в основном сугубо синтаксическая. MC>Т.е. ты предлагаешь прочитать все строки в список, а потом по нему пройтись? Это очевидное решение, но оно не подходит, если строк очень много.
Т.е. строки читаются функцией retrlines() и отдаются в callback одна за одной.
Что есть штатный режим использования retrlines (в Python):
retrlines(self, cmd, callback=None) unbound ftplib.FTP method
Retrieve data in line mode. A new port is created for you.
Args:
cmd: A RETR, LIST, NLST, or MLSD command.
callback: An optional single parameter callable that is called
for each line with the trailing CRLF stripped.
[default: print_line()]
Returns:
The response code.
Я же просто предлагаю доработать как-то синтаксис чтобы вызов с использованием inline callback function declaration:
Здравствуйте, c-smile, Вы писали:
PM>>Буферизовать и отдавать по одной строке не проблема. Мне почему-то кажется, что автору темы не хотелось качать с ФТП-сервера больше, чем нужно.
CS>Ну во первых retrlines() по своей спецификации и так читает всё. Т.е. callback будет вызван для каждой CS>line.
Так в том и фокус, что нормальные продолжения или корутины позволяют взять обычную функцию и превратить ее в генератор при помощи соответствующего callback'а. См. пример z00n, он решает поставленную задачу на Lua.
c-smile wrote:
> переводится на JS как > retrlines("LIST", function(line){ .... = line;... } );
Это всё не то. Код "for( var line in retrlines...)" привели для примера. Суть в том, что нам нужна самостоятельная сущность iterator, c которой удобнее работать.
Если код несколько сложнее, скажем что-то типа
Т.е. если нужно не просто перебирать строки, а читать их по порядку, и это происходит изнутри какого-то хитрого сложного алгоритма, то тупо переиначить код на использование callback не выйдет. А уж если нужно читать строки из нескольких итераторов сразу... брр:
Здравствуйте, palm mute, Вы писали:
PM>Здравствуйте, c-smile, Вы писали:
PM>>>Буферизовать и отдавать по одной строке не проблема. Мне почему-то кажется, что автору темы не хотелось качать с ФТП-сервера больше, чем нужно.
CS>>Ну во первых retrlines() по своей спецификации и так читает всё. Т.е. callback будет вызван для каждой CS>>line.
PM>Так в том и фокус, что нормальные продолжения или корутины позволяют взять обычную функцию и превратить ее в генератор при помощи соответствующего callback'а. См. пример z00n, он решает поставленную задачу на Lua.
Ну как сказать... использовать coroutine для перебора элементов некоего stream это себя не любить.
corouitine это отдельный stack. Имплементация оных (corroutines) и в Lua и в Python (generators/yield) выполнена как т.н. называемые "green" thread (a.k.a. cooperative threads, a.k.a. fibers).
В принципе green thread значительно легче чем OS thread но тем не менее это не самая простая операция.
Здравствуйте, ., Вы писали:
.>c-smile wrote:
>> переводится на JS как >> retrlines("LIST", function(line){ .... = line;... } ); .>Это всё не то. Код "for( var line in retrlines...)" привели для примера. Суть в том, что нам нужна самостоятельная сущность iterator, c которой удобнее работать.
Роман задал конкретный вопрос который можно сформулировать след. образом:
Как трансформировать delegated-iteration в inline-iteration.
Пусть iB это блок кода который нужно выполнить на каждой итерации.
Тогда логику итерации для delegated-iteration можно описать как F(iB(el))
а inline-iteration соответственно вот это (el=F(),iB(el)).
Разница между этими двумя логически эквивалентными способами итерации
состоит в структуре стека (ну или последовательности вызовов):
F(iB(el)) на стеке лежит F frame а затем iB frame.
el=F(),iB(el) — F и iB frames появляются на стеке попеременно.
.>Если код несколько сложнее, скажем что-то типа .>
.>iter = retrlines(ftp...)
.>l1 = iter.next()
.>if(l1 == 'a'):
.> l2 = iter.next()
.> l3 = iter.next()
.> doSomething(l2, l3, iter)
.>else
.> doSomething2(iter.next())
Есть два способа:
1) переделать саму функцию retrlines чтобы действительно можно было писать вот так:
[code]
iter = retrlines()
l1 = iter.next()
т.е. она должна возвращать итератор (т.е. ссылку на функцию getNextValue())
а не делать итерацию на своем стеке.
либо при сохранении текущей сигнатуры retrlines использовать способ brute force (2):
загоняем retrlines в отдельный thread и подсовываем в качестве callback функцию делающую
context switch на каждой итерации. Это то что делает coroutine.wrap в LUA.
Очевидно что это не есть самый эффективный способ рвать зубы.
Что-то мне говорит что есть еще пара способов такой трансформации ... думать надо короче.
.>switch(iter.next()) .>... .>[/code] .>Т.е. если нужно не просто перебирать строки, а читать их по порядку, и это происходит изнутри какого-то хитрого сложного алгоритма, то тупо переиначить код на использование callback не выйдет. А уж если нужно читать строки из нескольких итераторов сразу... брр: .>
c-smile wrote:
> Роман задал конкретный вопрос который можно сформулировать след. образом: > > Как трансформировать delegated-iteration в inline-iteration.
Ладно, может я не так понял. Но если это правда об этом, т.е. синтаксическое "преобразование", то неинтересно. А вот когда кто-то упомянул корутины — стало интересней.
> загоняем retrlines в отдельный thread и подсовываем в качестве callback > функцию делающую > context switch на каждой итерации. Это то что делает coroutine.wrap в LUA. > Очевидно что это не есть самый эффективный способ рвать зубы.
Как я понимаю, в LUA корутины использую cooperative threads, что сильно упрощает жизнь — не надо использовать локи, нет никакого параллельного выполнения, системных вызовов. Просто переключаются стеки, context не такой страшный — если я правильно понимаю — указатель на текущий стек. А в более других языках это делается ещё проще.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, z00n, Вы писали:
Z>Здравствуйте, c-smile, Вы писали:
CS>>Ну как сказать... использовать coroutine для перебора элементов некоего stream это себя не любить. Z>Здесь начинается ваше заблуждение.
Вполне возможно. Я просто сам в поисках оптимального решения поэтому может и смотрю "искоса низко голову наклоня".
CS>>corouitine это отдельный stack. Имплементация оных (corroutines) и в Lua и в Python (generators/yield) выполнена как т.н. называемые "green" thread (a.k.a. cooperative threads, a.k.a. fibers). CS>>В принципе green thread значительно легче чем OS thread но тем не менее это не самая простая операция. Z>Еrlang, Lua, Stackless Python, Limbo и.т.д используют VM threads — они очень дешевые ~600-1000 байт на поток, а скорость переключения контекста соизмерима со скоростью вызова функции. Хорошая реализация продолжений (как в Chez) будет не хуже.
Память и количество опреаций на переключение это не единственные ограничения.
Есть еще косвенная доп. нагрузка на GC например.
Z>Т.е. для луа цена вопроса: создание потока — ~300 ns, переключение ~100ns + 1K на стек (ns — это 10e-9 sec). Stackless по моим замерам аналогичен по скорости. Limbo в пару раз быстрее, но он статически типизирован.
Времена в абсолютном значении мало что говорят. Нужны относительные цифири.
Переключение кстати может быть вообще нулевым — вернее равным вызову функции в C.
coroutine (или generator в терминах Python) должна получить stack некоего размера в начале исполнения — 1k это хорошо, но
может потребоваться dynamic resize оного в runtime. Тоже что-то стоит.
Возвращаясь к исходной задаче идеальным наверное является рефакторинг самого метода retrlines
или использование оной как есть т.е. без ненужной for-in beautification.
Z>По поводу структурирования программ посредством микропотоков и передачи сообщений — есть интересная лекция того самого Роба Пайка. Сейчас он работает в Google — разрабатывает для них секретный язык Sawzall. Речь там идет о предтече Limbo, языке Newsqueak.
Z>Video: Advanced Topics in Programming Languages: Concurrency/message passing Newsqueak by Pob Pike
Это действительно интересно. Странно, как-то раньше это прошло мимо меня. Спасибо.
Но кстати как я понимаю эффективной имплементации этого дела так и нет.
Интерпретация UI модели конечно интересная, но в реалии требуется concurrent multitasking что в общем-то
так на так выходит. Ну и в принципе messaging используемый в GUI это и есть те самые channels.
Отказ от примитивов синхронизации в пользу messages — в этом действительно что-то есть как представляется.
Здравствуйте, palm mute, Вы писали:
PM>Здравствуйте, SergH, Вы писали:
SH>>Это всё-таки не вполне полноценные корутины, насколько я понимаю. Питоновская корутина должна возвращать итератор, т.е. объект с предопределённым интерфейсом, у него потом можно вызывать функцию next, а в конце он выбросит исключение. Для возвращаемого значения тут места нет. PM>Неполноценность питоновских генераторов в другом — ограниченность контекста одной процедурой, тем хорошо раскрыта в Lua wiki.
Интересная ссылка. Всегда обделял Lua вниманием, видимо зря .