Набрёл тут на статью, подумалось, что вот в C довольно нетривиальным способом (я бы назвал это хаком) решают вопрос, который в тех же ФЯ (с помощью ленивых списков) и C# 2.0 (с помощью yield return, если я ничего не путаю) решается на уровне языка вполне логично.
Т.е. мысль следующая:
на данном примере подтверждается, что языки отличаются по мощности предоставляемых абстракций и развитие идёт в сторону повышения степени абстракции.
Такая фича была в хранимых процедурах Interbase еще лет 5-6 тому назад. Но, я собственно не про это.
Интересно, как это укладывается в стековую модель. Как они со стеком то работают, когда процедура вроде бы передала управление вышестоящей процедуре, но и сама еще не завершилась.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, Курилка, Вы писали:
К>>yield return
S>Такая фича была в хранимых процедурах Interbase еще лет 5-6 тому назад. Но, я собственно не про это.
S>Интересно, как это укладывается в стековую модель. Как они со стеком то работают, когда процедура вроде бы передала управление вышестоящей процедуре, но и сама еще не завершилась.
А код почитать? Завершается процедура, там return по-твоему на что стоит? Просто хранится ещё "текущее состояние" так сказать, чтобы при следующем вызове продолжить с него. Так что нормально всё со стеком работает. Там же не используются longjump или типа того, используются обычные конструкции C (не вылезающие вроде бы как за рамки процедурного программирования, в отличие от всяких longjump). В чём ты видишь проблемы-то?
Здравствуйте, Курилка, Вы писали:
К>А код почитать? Завершается процедура, там return по-твоему на что стоит? Просто хранится ещё "текущее состояние" так сказать, чтобы при следующем вызове продолжить с него. Так что нормально всё со стеком работает. Там же не используются longjump или типа того, используются обычные конструкции C (не вылезающие вроде бы как за рамки процедурного программирования, в отличие от всяких longjump). В чём ты видишь проблемы-то?
Да я вообще-то про C#. Там же такие функции надеюсь "реентрант". И никакого контекста, в качестве дополнительного параметра, я думаю, не требуется, так что локальные переменные остаются в стеке (или где они там есть в таком случае).
Так что я бы сказал, что это достаточно хиртая фишечка. А если учесть еще откат стека при исключительных ситуациях...
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, Курилка, Вы писали:
К>>А код почитать? Завершается процедура, там return по-твоему на что стоит? Просто хранится ещё "текущее состояние" так сказать, чтобы при следующем вызове продолжить с него. Так что нормально всё со стеком работает. Там же не используются longjump или типа того, используются обычные конструкции C (не вылезающие вроде бы как за рамки процедурного программирования, в отличие от всяких longjump). В чём ты видишь проблемы-то?
S>Да я вообще-то про C#. Там же такие функции надеюсь "реентрант". И никакого контекста, в качестве дополнительного параметра, я думаю, не требуется, так что локальные переменные остаются в стеке (или где они там есть в таком случае). S>Так что я бы сказал, что это достаточно хиртая фишечка. А если учесть еще откат стека при исключительных ситуациях...
Про yield return не скажу, вот Vlad2 вроде бы мог сказать, если бы зашёл, а дополнительный параметр ты откуда взял? По-моему ты смысл не понимаешь идеи — нет никакого дополнительного параметра, передаваемого в функцию. Просто создаётся объект, который является конечным автоматом и внутри себя хранит состояние(и никакие это не локальные переменные, в том же варианте на сях это статическая(!) переменная, на уровне чего создаётся объект шарпа не совсем уверен, думаю что он должен создаваться в контексте вызывающего его кода). Но, т.к. я не вполне знаю как оно устроено внутри (с .нет 2.0 пока особо не ковырялся) поэтому не буду тебе рассказывать то, что точно не знаю
Прошу помощь зала
Здравствуйте, Курилка, Вы писали:
К>Про yield return не скажу, вот Vlad2 вроде бы мог сказать, если бы зашёл, а дополнительный параметр ты откуда взял? К>По-моему ты смысл не понимаешь идеи — нет никакого дополнительного параметра, передаваемого в функцию.
Все я понимаю. Я же говорю, что уже давным-давно такая фича была в Interbase и с ней я работал.
А дополнительный параметр — контекст я взял из твоей же статьи:
In a serious application, this toy coroutine implementation is unlikely to be useful, because it relies on static variables and so it fails to be re-entrant or multi-threadable. Ideally, in a real application, you would want to be able to call the same function in several different contexts, and at each call in a given context, have control resume just after the last return in the same context.
This is easily enough done. We arrange an extra function parameter, which is a pointer to a context structure; we declare all our local state, and our coroutine state variable, as elements of that structure.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, Курилка, Вы писали:
К>>Про yield return не скажу, вот Vlad2 вроде бы мог сказать, если бы зашёл, а дополнительный параметр ты откуда взял? К>>По-моему ты смысл не понимаешь идеи — нет никакого дополнительного параметра, передаваемого в функцию.
S>Все я понимаю. Я же говорю, что уже давным-давно такая фича была в Interbase и с ней я работал. S>А дополнительный параметр — контекст я взял из твоей же статьи:
Ну статья-то явно не моя
Дак вот я о чём тебеж рассказывал — в шарпе создаётся объект, хранящий этот самый контекст, соответсвенно не нужно ничего передавать (точнее то что нужно реализуется за кулисами компилятором), единственное, что вспоминается, что были вроде какие-то ограничения связанные с исключениями, что конкретно врать не буду)
Здравствуйте, Курилка, Вы писали:
К>Ну статья-то явно не моя
Да, конечно .
К>...в шарпе создаётся объект, хранящий этот самый контекст...
Вот в этом-то я сильно сомневаюсь . Скорее всего локальные переменные все таки хранятся в стеке. Вот пример из MSDN:
using System;
using System.Collections;
public class List
{
public static IEnumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
while(counter++ < exponent)
{
result = result * number;
yield return result;
}
}
static void Main()
{
// Display powers of 2 up to the exponent 8:foreach(int i in Power(2, 8))
Console.Write("{0} ", i);
}
}
Конечно, при вызове функции Power компилятором создается объект поддерживающий интерфейс IEnumerable, но то, что локальные переменные counter и result являются полями этого объекта — я сильно сомневаюсь. Хотя...
Здравствуйте, Курилка, Вы писали:
К>Набрёл тут на статью, подумалось, что вот в C довольно нетривиальным способом (я бы назвал это хаком) решают вопрос, который в тех же ФЯ (с помощью ленивых списков) и C# 2.0 (с помощью yield return, если я ничего не путаю) решается на уровне языка вполне логично.
Ну да, открыли Америку называется. И вместо того, чтобы не использовать С для вещей, для которых он не предназначен, автор предлагает эмулировать на нем продолжения, которые и на нормальном то языке не так легко понять. В результате программа будет выглядеть ужасающе и глючить будет по страшному.
К>на данном примере подтверждается, что языки отличаются по мощности предоставляемых абстракций и развитие идёт в сторону повышения степени абстракции.
Ясен пень, это только фанаты изобретать велосипеды на С/С++ с этим не согласны, поскольку они, как правило, ничего лучше и не видели.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, Курилка, Вы писали:
К>>Ну статья-то явно не моя
S>Да, конечно .
К>>...в шарпе создаётся объект, хранящий этот самый контекст...
S>Вот в этом-то я сильно сомневаюсь . Скорее всего локальные переменные все таки хранятся в стеке. Вот пример из MSDN:
... S>Конечно, при вызове функции Power компилятором создается объект поддерживающий интерфейс IEnumerable, но то, что локальные переменные counter и result являются полями этого объекта — я сильно сомневаюсь. Хотя...
А чего бы им не быть полями? Функция -> объект, переменные -> члены, имхо всё очень логично...
Все зависит от задачи, гдето вполне можно обойтись и конечными автоматомаи, но есть достатчно много классов, где нужен рекурсивный обход с замороченной логикой, конечно так или иначе все сведется к стеку, но пользовательский стек может быть намного сложнее, а переключение контекстов в любом случае затратная операция, но намного проще в применении (сохранение стека, регистров ).
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, stalcer, Вы писали:
S>Интересно, как это укладывается в стековую модель. Как они со стеком то работают, когда процедура вроде бы передала управление вышестоящей процедуре, но и сама еще не завершилась.
Совсем необязательно, чтобы вызов каждой функции обязательно шел через стек. На ФЯ можно написать большую программу, которая вообще не будет использовать стек для вызова функций. Если все функции хвостово-рекурсивные, компилятор проведет оптимизацию и вызов функций будет выглядеть как jump.
В C# yield сделан иначе — через конечный автомат. В этом случае процедура просто перед выходом запоминает свое текущее состояние и при следующем входе возвращается в него.
In a serious application, this toy coroutine implementation is unlikely to be useful, because it relies on static variables and so it fails to be re-entrant or multi-threadable. Ideally, in a real application, you would want to be able to call the same function in several different contexts, and at each call in a given context, have control resume just after the last return in the same context.
This is easily enough done. We arrange an extra function parameter, which is a pointer to a context structure; we declare all our local state, and our coroutine state variable, as elements of that structure.
Современные языки берут на себя весь этот тяжкий труд по созданию локального контекста, передаче его в функцию и т.п. Зачем копать яму лопатой на С, когда давно уже есть эскаваторы?
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, Serginio1, Вы писали:
S>> 1. На фиберах http://msdn.microsoft.com/msdnmag/issues/03/09/CoroutinesinNET/default.aspx S>> для рекурсивного обхода
S>То есть, все равно с использованием отдельного стека.
Для рекурсивного обхода без него никак не обойтись. Под рекурсивным здесь понимается сохранение состояния, для вызова его в нужное время. Прошу прощение за косноязычность.
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Serginio1, Вы писали:
S> Все зависит от задачи, гдето вполне можно обойтись и конечными автоматомаи, но есть достатчно много классов, где нужен рекурсивный обход с замороченной логикой, конечно так или иначе все сведется к стеку, но пользовательский стек может быть намного сложнее, а переключение контекстов в любом случае затратная операция, но намного проще в применении (сохранение стека, регистров ).
Если язык поддерживает концепцию продолжений, все эти рекурсивные обходы делают легко и приятно. И не нужно никаких фиберов, пользовательских стеков и других костылей.
Здравствуйте, Serginio1, Вы писали:
S>>То есть, все равно с использованием отдельного стека. S> Для рекурсивного обхода без него никак не обойтись. Под рекурсивным здесь понимается сохранение состояния, для вызова его в нужное время. Прошу прощение за косноязычность.
Еще раз повторюсь — стек не нужен, если есть встроенные в язык continuations. И даже если их нет, то без него можно обойтись, если наложить определенные ограничения на функции (исключить обычные вызовы функций и использовать только прямые переходы).
Здравствуйте, Quintanar, Вы писали:
Q>Если язык поддерживает концепцию продолжений, все эти рекурсивные обходы делают легко и приятно. И не нужно никаких фиберов, пользовательских стеков и других костылей.
А нука покажи рекурсивный обход на FW 2. Во всяком случае Влад чего то промолчал.
Да больше интересует сама реализация этой концепции продолжений для рекурсивного обхода. А там могут быть и фиберы, пользовательские стеки или ....
... << RSDN@Home 1.1.4 beta 4 rev. 303>>
и солнце б утром не вставало, когда бы не было меня