Модераторы: Sardar, Aliance

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> closure - замыкание, сокрытие, приближение? как корректнее перевести 
:(
    Опции темы
Zeroglif
Дата 24.11.2006, 12:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 644
Регистрация: 22.9.2005

Репутация: 28
Всего: 66



Цитата(SelenIT @  24.11.2006,  01:31 Найти цитируемый пост)
Zeroglif, а у меня наверное оффтоп, но очень-очень близкий: как по-русски можно грамотнее всего "обозвать" Scope chains?


Коротко фиг обзовёшь. Вот, например, как совсем не коротко, но зато метко описывается это дело у Харольда Абельсона и Джеральда Джея Сассмана (Структура и интерпретация компьютерных программ):

Цитата

Lexical scoping - лексическая сфера действия переменных, правило лексической сферы говорит, что свободные переменные в процедуре ссылаются на связывания в объемлющих определениях процедур; то есть они ищутся в окружении, в котором процедура определена. Окружение - это последовательность кадров. Каждый кадр есть таблица связываний, которые сопоставляют имена переменных соответствующим значениям. Значение переменной по отношению к данному окружению есть значение, которое находится в связывании для этой переменной в первом кадре окружения, содержащем такое связывание.

В том же русле, но уже на базе родного ECMAScript сжато я бы сказал так:

Цитата

Scope chain - это формируемая при исполнении функции/программы цепь объектов, которые при вычислении идентификатора последовательно опрашиваются на наличие свойства, имя которого совпадает с именем идентификатора.

Если совсем закоротить для удобства:

Цитата

Цепь объектов в лексическом окружении.

Ещё короче не горазд. smile


Это сообщение отредактировал(а) Zeroglif - 24.11.2006, 12:24
PM MAIL WWW   Вверх
SelenIT
Дата 24.11.2006, 13:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 49
Всего: 401



AKS, Zeroglif, спасибо! Zeroglif, Ваше второе определение вообще блеск, лучше для понимания, наверное, сформулировать просто невозможно. ++


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
12345c
Дата 25.2.2007, 23:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Круглый
****


Профиль
Группа: Vingrad developer
Сообщений: 2018
Регистрация: 26.12.2005
Где: наша не пропадала ?

Репутация: 57
Всего: 101



А исходное понятие closure лучше перевести (точнее, переназвать) как связывание или функция со связанными переменными.

Или ещё полнее, функция со связанными временными переменными (которые закончили своё существование).


(При этом, если такого связывания нет, логичнее называть function функцией, как все делают, а не озадачивать народ глобальным утверждением, что в JS все функции - closures.)
PM WWW   Вверх
Zeroglif
Дата 26.2.2007, 03:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 644
Регистрация: 22.9.2005

Репутация: 28
Всего: 66



Цитата(12345c @  25.2.2007,  23:34 Найти цитируемый пост)
как связывание или функция со связанными переменными

Наоборот. Если все переменные в функции (binding form) связаны (bound), то откуда тогда взяться замыканию. A вот ежели переменные не связаны своей функцией (free), то налицо оно самое, то есть, как минимум, функция должна быть со свободными переменными (опустим для простоты причуды с возможной eval-изацией строки в переменную)...

Цитата(12345c @  25.2.2007,  23:34 Найти цитируемый пост)
функция со связанными временными переменными (которые закончили своё существование)

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

Чтобы всё-таки вычленить замыкание из ряда остальных функций, что определённо имеет смысл, я бы сказал, что это функция, которая:

1) пережила контекст исполнения, в котором она была создана;
2) имеет на борту свободные переменные;

Два основных лейбла. Под переменными подразумевается понятно что.


Это сообщение отредактировал(а) Zeroglif - 26.2.2007, 03:25
PM MAIL WWW   Вверх
12345c
Дата 26.2.2007, 12:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Круглый
****


Профиль
Группа: Vingrad developer
Сообщений: 2018
Регистрация: 26.12.2005
Где: наша не пропадала ?

Репутация: 57
Всего: 101



Я имел в виду связывание с константой или значением, оставшимся после закрытия переменной. Т.е., в терминах "free", это возможно как раз с free vars. Которые можно описать как определённые вне функции, но временные, не глобальные. Не параметры своей функции и не создавшиеся в ней.

Что считать free variables - локальные и глобальные или только локальные? Как я понимаю, свойства closure проявляются только с локальными free.

Тогда, если термин "связывание" занят под противоположным действием - связыванием с переменными, то как назвать связывание с константой после закрытия (удаления) переменной?
PM WWW   Вверх
AKS
Дата 26.2.2007, 12:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Участник форума
**


Профиль
Группа: Участник
Сообщений: 725
Регистрация: 20.9.2006

Репутация: 27
Всего: 52



А может быть под термином closure нужно понимать совсем другое "явление природы"?
Может быть замыкание - это факт существования Activation/Variable объекта outer-функции в св-ве [[Scope]] inner-функции?
А момент, когда на объект inner-функции не останется ссылок и Activation/Variable объект outer-функции станет доступным для удаления из памяти, можно назвать "концом жизни" замыкания.
PM MAIL   Вверх
Zeroglif
Дата 26.2.2007, 13:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 644
Регистрация: 22.9.2005

Репутация: 28
Всего: 66



Цитата(12345c @  26.2.2007,  12:21 Найти цитируемый пост)
Которые можно описать как определённые вне функции, но временные, не глобальные.

Понимаешь, в этом смысле что глобальные, что "временные" технически суть одно и то же, если смотреть на них из-под замыкания, которое видит только строгую цепочку объектов в [[Scope]].

Цитата(12345c @  26.2.2007,  12:21 Найти цитируемый пост)
Что считать free variables - локальные и глобальные или только локальные? Как я понимаю, свойства closure проявляются только с локальными free.

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

Цитата(12345c @  26.2.2007,  12:21 Найти цитируемый пост)
как назвать связывание с константой после закрытия (удаления) переменной

В том и дело, что переменная (имя) не удаляется, если живёт замыкание, то живёт и [[Scope]], живут и все свободные переменные (имена). Технически (не углубляясь в специфику) переменную связывает определённый Variable Object, который создаётся один раз и не умирает, т.к. он сильно нужен замыканию, ибо оно может обращаться к свободным переменным, пытаясь найти связь в одном из Variable Objects.

p.s. вышеописанное - есть смесь чуждых и не чуждых javascript терминов smile

Добавлено @ 13:12 
Цитата(AKS @  26.2.2007,  12:45 Найти цитируемый пост)
А может быть под термином closure нужно понимать совсем другое "явление природы"? Может быть замыкание - это факт существования Activation/Variable объекта outer-функции в св-ве [[Scope]] inner-функции?

Именно так, я уже писал раньше в этой ветке, что функциональная терминология немного притянута за уши (ну, и бог с ней). Важен не только факт существования Activation/Variable объектов, важна и множественность такого существования под каждое замыкание...
PM MAIL WWW   Вверх
12345c
Дата 26.2.2007, 13:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Круглый
****


Профиль
Группа: Vingrad developer
Сообщений: 2018
Регистрация: 26.12.2005
Где: наша не пропадала ?

Репутация: 57
Всего: 101



Цитата(Zeroglif @  26.2.2007,  13:07 Найти цитируемый пост)
В том и дело, что переменная (имя) не удаляется, если живёт замыкание, то живёт и [[Scope]], живут и все свободные переменные (имена).
Действительно, небольшой пример это показывает:
Код
<script>
f1=function(){
  var i=1
  f3=function(){return i;} //для "усиления эффекта" ставим опр. f3 выше
  f2=function(){return ++i;} //меняем i, чтобы увидеть связанность его с f3
}
f1(); //делаем f2,f3 с сохранённым i
alert([f2(),f3()]) // даст 2,2 , что оБнаружит общий указатель на бывший i
</script>


Добавлено @ 13:50 
Цитата(Zeroglif @  26.2.2007,  13:07 Найти цитируемый пост)
Понимаешь, в этом смысле что глобальные, что "временные" технически суть одно и то же, если смотреть на них из-под замыкания, которое видит только строгую цепочку объектов в [[Scope]].
Да, но глобальные переменные не вызывают ощущения "чудес", если читатель кодов не знает про особенности функций в JS. Стоит глобальная переменная и стоит, оставаясь такой. Интуитивно предполагается, что глобальная не должна становиться значением в момент определения функции, а локальная должна, потому что известно, что она прекратит существование. Создатели поступили наоборот: живут все как переменные, более того, временные продолжают жить. Ну зомби натуральные.

PM WWW   Вверх
Sardar
Дата 26.2.2007, 13:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Самое интересное, что легко узнать ещё на момент трансляции все переменные, используемые внутри closure. Отсюда действительно можно сделать "оптимизацию", физически разделив представление closure (функция связанная с лексическим контекстом) и функции (свободная функция).


12345c, в примере ты создал функции в переменных f2 и f3, не объявленных ранее, что привело к их автоматическому созданию в "самом верхнем контексте" a.k.a global. Это не очень хороший приём, т.к. засоряет global пространство имён.

Добавлено @ 14:01 
Цитата(12345c @  26.2.2007,  12:41 Найти цитируемый пост)
если читатель кодов не знает про особенности функций в JS

Знакомые с любым декларативным и"полу-декларативным" языком сразу всё поймут smile

Цитата(12345c @  26.2.2007,  12:41 Найти цитируемый пост)
живут все как переменные, более того, временные продолжают жить. Ну зомби натуральные.

Лучше всего понять это разделив переменную на значение и ссылку на значение. Объявляя переменную мы объявляем ссылку, присваивая значение мы изменяем ссылку на новое значение. Когда объект значение не имеет более ни одной ссылки, то он удаляется сборщиком мусора. Closure держит ссылки на все используемые значения, потому последние из памяти не удаляются, до тех пор, пока хоть кто нибудь ссылается на сам closure.

Кстати, раз много-поточности в JS нет, то сборщик мусора реализуется элементарным подсчётом ссылок при каждой операции присвоения.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Sardar
Дата 26.2.2007, 14:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Цитата(Sardar @  26.2.2007,  12:56 Найти цитируемый пост)
Самое интересное, что легко узнать ещё на момент трансляции все переменные

Впрочем тут нужна поправка, в случае настоящего JS полностью отбросить ссылку на родительский контекст нельзя (тем самым освободив ссылки на все не используемые значения, а следовательно возможно освободив и сами значения). А всё потому что eval должен видеть все переменные, как и "обычный код":

Код
function test(c) {
    var a = 90;
    var b = 70;
    function cool(c) {
        alert('In: '+ (a+=5));
        eval("alert('In: ' + (b+="+c+"));");
    }
    cool(c);
    alert('Out: '+a);
    alert('Out: '+b);
}
test(5);


В примере видим, что для cool необходим только а, а b необходима не явно (отследить транслятором нельзя). Если реализовать функции и контексты эффективно, то мы отбросим контекст test, тогда eval должен вывести NaN (автоматом созданная b в global будет undefined, последующая арифметика в NaN). Но бродилки "правильно" выполняют код, показывая что все родительские контексты реально сохраняются. Впрочем транслятор может быть на столько умён, что бы отслеживать появление eval и сохранять контексты тогда, когда это нужно. Но это не оправдывает излишней свободы eval, препятствующей эффективной по памяти реализации closure/функций.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
12345c
Дата 26.2.2007, 22:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Круглый
****


Профиль
Группа: Vingrad developer
Сообщений: 2018
Регистрация: 26.12.2005
Где: наша не пропадала ?

Репутация: 57
Всего: 101



Этот пример ничего не показывает - до выхода из test перем. b сохраняется. Модифицируем:

Код
<script>function test(c) {
    var a = 20;
    var b = 10;
    prnt("Вызвана test("+c+")");
    ff=function(c) {
        prnt("Вызвана ff("+c+")");
        prnt('a++: '+ (a++));
        eval("prnt('b+=\"c\": ' + (b+="+c+"));");
        prnt("Выход из ff("+c+")");
    }
    ff(c);
    prnt('Out_a: '+a);
    prnt('Out_b: '+b);
    prnt("Выход из test("+c+")");
}

onload=function(){
  test(5);
  ff(3);
}
prnt=function(a){document.body.innerHTML+=a+'<br>';}
</script>
Он действительно показывает, что b сохраняется.
Код
Вызвана test(5)
Вызвана ff(5)
a++: 20
b+="c": 15
Выход из ff(5)
Out_a: 21
Out_b: 15
Выход из test(5)
Вызвана ff(3)
a++: 21
b+="c": 18
Выход из ff(3)

Но весь сыр-бор не для этого, а чтобы проверить, как влияет eval на скорость операций. Допишем:
Код
<title>Check scope saving for eval operators in closures</title>
<script>
function test(c,if1,n){
    var a = 20;
    var b = 10;
    ff=function(c){
        a++;
        if(!if1)eval("prnt('b+=\"c\": ' + (b+="+c+"));");
    }
    for(var i=0;i<n;i++)ff(c);
}
function test2(c,if1,n){
    var a2 = 20;
    var b2 = 10;
    ff2=function(c){
        a2++;
        if(!if1)a2+=c;
    }
    for(var i2=0;i2<n;i2++)ff2(c);
}


onload=function(){
tt=new Date().getTime();
  test(5,1,500000);
  ff(3,1,100);
  prnt('eval.time='+(new Date().getTime()-tt));
tt=new Date().getTime();
  test2(5,1,500000);
  ff2(3,1,100);
  prnt('eval.time2='+(new Date().getTime()-tt));
tt=new Date().getTime();
  test(5,1,500000);
  ff(3,1,100);
  prnt('eval.time='+(new Date().getTime()-tt));
tt=new Date().getTime();
  test2(5,1,500000);
  ff2(3,1,100);
  prnt('eval.time2='+(new Date().getTime()-tt));
}
prnt=function(a){document.body.innerHTML+=a+'<br>';}
</script>
<div>Проверим, насколько разумен браузер в забывании неиспользуемого окружения. <br><br>

Он не знает, что будет под eval, поэтому для "корректной" работы должен запомнить всё окружение.
</div>
Посмотрим, что будет быстрее: функция с eval, но не выполняющая его из-за выбора параметра (но транслятор должен учесть, что в теле eval есть). Или функция без eval с тем же объёмом операций.

2 пары - потому что они имеют обыкновение останавливаться и спрашивать: работать ли дальше?
Результаты говорят, что обе функции работают с одинаковой скоростью, наличие eval не усугубляет время выполнения (IE6, FF2).

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

Сделать конструктор временного массива вместо b? (Почти доказано, что в окружении он сохранится.) Устроить утечку памяти и измерять скорость её роста? Второе интересно. Если сделать функцию, отхватывающую по 1-10 К памяти на временные переменные, и создавать такие функции в цикле, то по скорости роста памяти можно сказать, образумливается когда-либо браузер с запоминанием окружения или нет, очищает ли он когда-либо память окружения.

Это сообщение отредактировал(а) 12345c - 27.2.2007, 15:51
PM WWW   Вверх
Sardar
Дата 26.2.2007, 23:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Цитата(12345c @  26.2.2007,  21:40 Найти цитируемый пост)
Этот пример ничего не показывает

Цитата(12345c @  26.2.2007,  21:40 Найти цитируемый пост)
Но весь сыр-бор не для этого, а чтобы проверить, как влияет eval на скорость операций.

Нет, пример был что бы показать не возможность "отброса лишнего" при трансляции кода в байткод. Именно потому, что в JS код уже не является константой, а может модифицироваться из-за eval (тело функции другое из-за eval). Следовательно транслятор не способен определить какие ресурсы использует функция до этапа выполнения (изыскания на эту тему в фунегоидных языках). Другими словами реализуя интерпретатор JS я не смогу реализовать сохранение контекстов эффективно (в примере контекст от test будет висеть в памяти, удерживая ещё и b, хотя это не всегда нужно).

Это к тому, что из-за такой казалось бы малой фичи как полная видимость переменных в eval делает код JS сложно транслируемым в бинарник. Просто мысли, к реальным браузерам не имеет отношения. Твой тест всего лишь показывает, что транслятор бродилки ипосльзоуемой в тесте возможно не так умён, что бы выявить closure без eval (возможно он вообще AST дерево эвалюирует), хотя такая фишка не очень сложна в реализации  smile 


P.S. все мои мысли по поводу реализации JS из-за прошедшей лабы, написал язык Forthy, где объекты и closure реализованы почти как в JS. Eval не реализовывал (возможен парсинг и выполнение в рантайме, но видимы будут только используемые статическим кодом переменные), естественно контексты вышли фиксированными по размеру с линковкой по ссылке на этапе трансляции - читай шустро как в C'ях smile


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
12345c
Дата 27.2.2007, 12:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Круглый
****


Профиль
Группа: Vingrad developer
Сообщений: 2018
Регистрация: 26.12.2005
Где: наша не пропадала ?

Репутация: 57
Всего: 101



Это понятно, что мысли - для желающих написать свой язык. А ты свой переделывал в Пай-код? Или делался с расчётом на возможность трансляции до машинного?

Трансляция с eval видится возможной в виде гибрида - известный код транслируется, а в случае eval остаётся окружение с интерпретатором. И привязка машинных объектов к окружению (представление данных будет другим, поэтому надо иметь процедуру чтения машинного представления данных в интерпретаторе и наоборот).

Но, возвращаясь к коду в браузерах, не очень приятное "открытие", что сохраняется весь контекст временных переменных в closure. Осталось выяснить, без eval он также будет сохранять весь контекст?
PM WWW   Вверх
Sardar
Дата 27.2.2007, 14:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 78
Всего: 317



Цитата(12345c @  27.2.2007,  11:27 Найти цитируемый пост)
А ты свой переделывал в Пай-код? Или делался с расчётом на возможность трансляции до машинного?

Да в байткод (p-код). Его можно развернуть до машинного, но по сути будет масса вызовов функций (или threaded код), опкоды высокоуровневы (стек ориентированны). На следующей лабе попробую реализовать интерпретатор с JIT'ом (за одно попробую регистр-ориентированный p-код, как в parrot)

Цитата(12345c @  27.2.2007,  11:27 Найти цитируемый пост)
поэтому надо иметь процедуру чтения машинного представления данных в интерпретаторе и наоборот

А ничего совершенно не отличается, если только не применять через-чур агрессивные оптимизации, а их не просто реализовать. Контексты функций это фиксированные по размеру массивы (линковка по ссылке), объекты это хеш-таблицы (линковка в рантайме по имени), переменные это вероятней всего tagged ссылки и т.д.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
12345c
Дата 27.2.2007, 16:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Круглый
****


Профиль
Группа: Vingrad developer
Сообщений: 2018
Регистрация: 26.12.2005
Где: наша не пропадала ?

Репутация: 57
Всего: 101



Вношу коррективу: вначале я измерял время по последнему скрипту не совсем правильно - вызывал одну и ту же первую функцию с eval по недосмотру. Сейчас исправил код, уменьшил счётчик в 10 раз, чтобы не было остановов в FF, и вот что FF2 выдал:

Код
eval.time=2688
eval.time2=187
eval.time=2625
eval.time2=188
Нечётные времена в мсек - c eval, чётные - без. Разница отлично заметна. Значит, интерпретатор учитывает наличие eval в коде функции и сильно тормозит, несмотря на то, что код eval не исполняется. В IE6 другая картина:
Код
eval.time=156
eval.time2=156
eval.time=141
eval.time2=140
 - ему всё равно, он работает с одинаковой скоростью и всегда немного быстрее.
"Самый быстрый в мире" Opera9.0 работает типично так:
Код
eval.time=266
eval.time2=62
eval.time=266
eval.time2=78
Тоже видна чувствительность к eval, но не столь катастрофичная. Всё это было с параметрами 
Код
  test(5,1,50000);
  ff(3,1,100);
...
  test2(5,1,50000);
  ff2(3,1,100);


Добавлено @ 16:18 
(И при этом в предыдущем эксперименте показано, что b сохраняется в IE в любом случае.)
PM WWW   Вверх
Страницы: (4) Все 1 2 [3] 4 
Ответ в темуСоздание новой темы Создание опроса
Форум для вопросов, которые имеются в справочниках, но их поиск вызвал затруднения, или для разработчика требуется совет или просьба отыскать ошибку. Напоминаем: 1) чётко формулируйте вопрос, 2) приведите пример того, что уже сделано, 3) укажите явно, нужен работающий пример или подсказка о том, где найти информацию.
 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | JavaScript: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0719 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.