Как стать автором
Обновить

Комментарии 118

это касается только императивных языков. Тот же SQL имеет огромную область применения но там нет никакого ООП или пошагового выполнения программы вообще.
SQL — это инструмент баз данных, речь в статье не об этом.
SQL это точно такой же язык программирования как C# или Haskell
Не такой же как C# или Haskell, но язык программировани, с Haskell в семействе декларативных, но реализация абсолютно различные, но это и так понятно.
это не так, попробуйте с помощью SQL нарисовать пользовательский интерфейс.
попробуйте нарисовать пользовательский интерфейс с помощью C# но без обращения к оконным библиотекам.
Это не корректный пример, обращения к подобным библиотекам есть часть языка, в SQL — это находится за областью действия языка.
батарейки != язык, в java 1.6 есть библиотека для concurrency, а в 1.5 ее не было и все писали ручками. То что ее потом внесли в набор поставляемых библиотек хорошо, но если я напишу очередную батарейку это будет реализацией идеи, но не частью языка, аналогично с графическими компонентами, swing, atk, gwt, swt это библиотеки для графики, написанные на языке.
Даешь библиотеку для работы с окнами на SQL?
Таблица команд с низкоуровней графикой и select ы к ней, было бы весело, но не нужно…
в том то и дело. У SQL — узкая область применения — о чем вам и говорю.
Область применения это одно, а парадигма реализации другое. Если я пишу язык общего назначения, то там будет много всего, но я могу писать DSL под вычисление нуклеотидных последовательностей как в декларативном стиле, так и в структурном, но область у меня ограничена этими вычислениями.

P.S: я уже начал писать статью про ФП, в ответ на ваш пост, надо еще ваши персептроны покавырять из lisp -а.
Антоним «декларативная парадигма программирования» — «императивная», а не «структурная».
очень даже корректный. На C# прекрасно пишутся ASP.NET сайты, но код запущенный на веб-сервере не имеет доступа к выводу на экран.
Или точнее так. Да SQL — это чисто структурное программирование, оно применяется в рамках своей области — реляционные базы данных для хранения долгоживущих объектов/данных. Задача программиста сделать абстракцию хранения в ООП-обертке, что же там будет на низком уровне не столь важно, т.е. реализация инкапсулирована. И конечно, в рамках работы с реляционными данными нет ничего лучшего SQL, но именно об этом и говорится в статье — специализированные подходы не должны вступать в противоречие с высокоуровневыми парадигмами. Так, например, есть совершенно жуткий стиль написания, когда обращения к базе и написание select`ов можно увидеть по всей программе не в хранимых процедурах, где этому место, а в функциях ОО-языков. Вот это и есть яркий пример нарушения специализированного подхода над структурным (и даже не ООП, в данном случае) парадигмой программирования. Нарушается принцип инкапсуляции. То есть в программе нет модели хранения в базе данных, это ошибочно считается просто синтаксисом языка программирования (аналогично тому как оператор for) — и в итоге конфликт специализированного подхода над парадигмой высокого уровня.
это ошибка с точки зрения ООП логики…
А не ошибка с какой?
По секрету скажу, что SQL как и прочие *языки запросов* может быть применён не только к реляционным структурам.
«Совсем никакого ООП» говорить нельзя про SQL. Мы моделируем предметную область в терминах таблиц, столбцов и строк практически так же, как и с помощью классов, свойств и объектов. Параллели отчётливо видны. Уровень абстракции данных один и тот же. По сути программирование на SQL находится на той же ступеньке «почти ООП», что и программирование на Си с активным использованием структур. А вычисляемые поля — это, практически, методы.

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

Тем более, что SQL язык скорее императивный по сути — все (?) команды начинаются с глагола в повелительном наклонении, указывающим машине что делать.
Императивный или декларативный подход не исключают структурной или объектно-ориентированной парадигмы.

плюсанул за это. остальное несколько спорно. Но главное действительно понимать, что декларативность не связана со структурной парадигмой — это разные вещи.
Ну это смотя какой SQL:
create or replace type body Address_Type
    as
        member function toString return varchar2 
        is 
        begin
            if (street_addr2 is not NULL) 
            then 
                return street_addrl || chr(10) || street_addr2 || chr(10) || city || ', ' || state || ' ' || zip_code;
            else
                return street_addrl || chr(10) || city || ', ' || state || ' ' || zip_code;
            end if; 
        end;
    end; 

select name, p.home_address.toString() from people P
Это как раз обычный SQL :), только вот любители функциональщины приписали заслуги SQL себе…
В смысле, объектно-реляционная модель и методы у объектов, хранимых в полях таблиц уже стали обычным делом? А как тогда назвать тысячи решений, когда всё ограничивается простыми таблицами с числами и строками + ORM-обёрткой на постороннем языке программирования?
я про другое, про использование if в SQL — ведь противоречит функциональному программированию?
SQL — это не язык общецелевого назначения! По крайней-мере SQL-92, а если смотреть на расширения обычный процедурный + SQL (язык запросов к БД).

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

Ссылка на Шилда и Буча — хорошо, но апологеты ООП защищают свои идеи, где другая сторона медали? Написать что ли про функциональщину в противовес, с заголовком «существует только lisp» =)
О какой другой стороне медали вы говорите?
В данном случае о функциональных языках, лямда исчислении… которые не вписываются в структурное программирование ни на каком уровне. Обратная сторона медали, это все то что помимо ООП и не притянуто за уши к нему или нагло забрано из функционального программирования и выдано как фича ООП.
см. upd. в статье
Почему не вписывается?
Это постепенное иерархическое разложение продолжается пока возникнет выделенные подпроблемы, которые достаточно просты, чтобы с ними справиться непосредственно. Вторая фаза парадигмы структурного программирования влечет за собой работу вверх от конкретных объектов и функций к более абстрактным объектам и функциям, используемые всюду в модулях, произведенных нисходящим проектированием.

Что-то по другому? Правда я не понял что объектно-ориентированном программирование другое…

По сути дела функциональные языки исходят из того, что объект программы это функция, а объектно-ориентированные, что объект — это данные + к ним привязаны некоторые методы обрабатывающие данные.
Декларативное-императивное, функциональное-процедурное. Функциональная программа вполне может быть структурной. Более того она будет ею с большей вероятностью, чем процедурная. Функциональные языки как-то больше способствуют декомпозиции по алгоритмам.
Функциональные языки как-то больше способствуют декомпозиции по алгоритмам.

Именно так. И это скорее минус.
Омг. Я даже не знаю, как это комментировать на самом деле. Пожалуйста, пожалуйста, почитайте любое введение в функциональное программирование (например John Harris «Introduction to functional programming» валяется в сети, не сложно найти). И книжку по теории типов, навроде Benjamin Pierce «Types and Programming Languages». Ну, чтобы было ясно, кто откуда куда вышел и кто более высоких уровней.
см. upd. в статье
Какое отношение скорость работы имеет к математической состоятельности подхода? Где и когда кто-то говорил, что ФП быстрее другой парадигмы? Как вообще парадигма может быть быстрее? Это нонсенс.
Более того, ни «события ООП», ни LINQ (О___О) никто в здравом уме не будет использовать в алгоритмах машинного обучения. Векторизация и линейная алгебра — наше всё. Можно сказать, что молотком плохо получается жестяные банки открывать, тоже отличный аргумент будет.
У меня нет ни времени, ни желания, ни даже места пересказывать здесь то, что вы можете найти в тех книгах, на которые я вам указал.
Если хотите узнать про то, как ФП используется, и для каких «небольших» классов задач оно подходит, то вот пара ссылок:
Узнаём, откуда ФП есть пошло
Не забываем, что основная его идея — что результат = программа(входные данные).
Узнаём, что делает его очень интересной парадигмой
Узнаём, кто его использует
Узнаём, почему его используют
Узнаём, что такое побочные эффекты и почему в чистых функциональных языках нет переменных, и думаем, как это можно «подчинить» императивным парадигмам.
Узнаём, на какой идее на самом деле основано всё «высокоуровневое» ООП, кроме идей абстракции, которые существуют с начала существования человечества.
Узнаём, что на этом свет не сошёлся
Узнаём, почему математики любят ФП
Ну, и так:
Closure
«Closures can be used to implement object systems.» — обратите внимание
для каких «небольших» классов задач оно подходит — ну вот и все. Вы сказали тоже самое, о чем я в статье.
а это вы так шутите… ну зря. это действительно очень узкая область
сводится к этому «почему математики любят ФП»
«никто в здравом уме не будет использовать в алгоритмах машинного обучения. Векторизация и линейная алгебра — наше всё.» — а это просто не серьезно, и пример вам в статье про перцептрон.
«почему в чистых функциональных языках нет переменных»

это вообще не серьезно :), это огромный минус языка.
Пожалуйста, перестаньте. Я сейчас просто от смеха умру, ей богу xDDD В следующий раз потратьте хоть немного времени на изучение того, о чём пишите, хорошо?)
Зачем, на глупости тратить время. Напишите свою краткую статью — почитаю. Но в язык без переменных — это глупость, это скатывание к более низкоуровневому программированию и ничего больше.
Наоборот, это более высокий уровень абстракции. В ассемблере (вернее архитектуре популярных процессоров) переменные есть. Функциональные языки вынуждены их использовать для реализации, потому что нет функций в ассемблере, есть только подпрограммы.
ну над ассемблером может быть, и только
Языки оперирующие переменными ближе к ассемблеру, чем ими не оперирующие. Отчасти эти объясняется низкое быстродействие функциональных.
Т.е. низкое быстродействие функциональных — это установленный факт? Что ж хорошо. И тогда спрашивается лишь одно — если это медленно работает, и не естественно на этом создавать реальные модели предметных областей. То есть ли хоть одно преимущество? В чем?
Для некоторых случаев функциональные языки работают медленнее. Тем не менее, смотрите слайд 11.
Тем не менее, любой статически типизированный функциональный язык с лёгкостью обойдёт по производительности среднестатистического кода Perl, PHP или Python с Ruby. Видимо, всем пора прекратить использовать эти языки, судя по вашей логика.
Скажите, вы прочитали информацию хоть по одной из тех ссылок, что я вам кинул?
Вы мне назовите вначале хоть одно преимущество, а не придуманные фикции.
Why functional programming matters
Читаем раздел benifits of F#
Why functional programming?
Ну, или смотрим как делают асинхронное программирование в функциональном языке и как — в ОО.
Я могу долго продолжать, но поймите: если вы пришли устраивать революцию, то это вы должны аргументировать. Я пока не вижу никаких аргументов кроме «я не знаю, что такое ФП, поэтому оно плохое».

Да вы мне не ссылки кидайте, все что нужно я прочитал. Вы мне ответьте четко и ясно по пунктам.

Революцию я тут не устраиваю, на самом деле в статье изложил всем известные вещи, но оказалось просто много не согласных. И я не могу понять только одного — ради чего вы отказываетесь от ООП? Ради чего? Скорости — выяснили, что нет. Тогда ...?
В ссылках написано всё по пунктам. Я вам так напомню, что я вам ничего не должен — пора бы уже научиться читать и пользоваться гуглом, ей богу.
С чего это вы решили, что я отказываюсь от ООП? Я умею и люблю использовать все основные парадигмы программирования.
А так значит не отказываетесь? Ну и как вы совмещаете ООП с функциональным программированием? Вам же нравится без переменных программировать, так зачем ООП?

P.S.
В ссылках этого нету.
Вообще-то F# (которым пользуюсь я) совмещает ООП и ФП, как и Scala, как и OCaml.
Я спрашиваю КАК совмещаете (а не на чем)? И ради чего?

Т.е. Вы пишите в классах или нет? Вы используете переменные или нет? Когда вы решаете, что надо использовать функциональные выражения? Для чего вы их используете? Нарушает ли ваше использование принципы ООП? Чему вы отдаете предпочтение, сделать все равно через функциональное программирование и нарушить ООП, или нет, останавливаетесь и там где функциональное программирование нарушает ООП, соблюдаете вначале ООП?

Вот о чем речь…
А в курсе ли вы, что такая вещь, как шаблоны проектирования, которые — суть ООП — имеют в себе серьезные функциональные корни?

Знаете ли вы значение и обоснование тех слов, которыми оперируете, — вот о чем речь…
Да, и какие же там функциональные корни?
Вам будет полезнее выяснить это самому.
сами не знаете что говорите просто, в сказки верите
Пожалуйста, возьмите любой учебник по любому из этих языков и выучите его. И все ваши вопросы отпадут сами собой, уверяю. Вот конкретно вам я рекомендую вот эту книгу, где подробно показано, как ФП и ООП пересекаются и как их использовать вместе.
Вы также можете посмотреть мою статью, которая в неком смысле касается этого. Но это ни в коей мере не заменит вам полноценное изучение предмета, конечно.
Думаю, вам просто нечего ответить, поэтому и посылаете в гугл…
И да вы мне отвечайте на вопрос ни как, что-то сделать, или какие у вас там есть внутренние термины. Это все равно, чтобы я вам говорил «а знаете, что такое наследование», «посмотрите как реализовать полиморфизм»… это же детский лепет. Я спрашиваю ради чего?
Вы можете хоть на один из этих вопросов ответить? Я задал их потому что мне кажется, что вы не знакомы с элементарными основами функционального программирования, и поэтому вы говорите такие, например, глупости:
«Но в язык без переменных — это глупость, это скатывание к более низкоуровневому программированию и ничего больше.»
Вы умеете пользоваться браузером и читать простые английские тексты, я вообще не понимаю этой настойчивости: там везде bullet-points, в чём проблема-то?
Зачем мне переписывать то, что уже написано сотни раз? Может, лучше вам взять и почитать, наконец, хоть что-то про ФП?
Вот так «низкоуровнево», например, выглядит применение функции высшего порядка fold (которая объявлена без переменных), к списку (который неизменяемая структура данных) с целью сложения его элементов:
List.fold (+) 0 [1..10000]
Да, это такой «низкоуровневый» аналог императивного «высокоуровнего» решения:
let mutable sum = 0
let array = [|1..10000|]
for i in 0..(array.Length - 1) do
sum <- sum + array.[i]

Вы можете считать глупостью, конечно, работы таких учёных, как Рассел, Чёрч или Карри. Уверяю вас, они от этого ничего не потеряют. Как и мы магическим образом не станем писать длинный и загромождённый переинженерингом код, чтобы только инкапсулировать операцию, как в паттернах Command или Strategy, или чтобы создать объёмную и унылую иерархию классов в паттерне State, вместо того, чтобы воспользоваться алгебраическими типами данных и сопоставлением с образцом. Каждому своё: если вы хотите уметь пользоваться только молотком, это ваш выбор.
Да и читал я — не убеждает, смеяться хочется от надуманных проблем и еще более смешного решения
Вообще и в математике (по крайней мере изученной мною) тоже нет переменных в «тьюринговом» смысле слова (именованная область памяти). Это огромный недостаток математики? :)
Математика тут не причем. Это заблуждение, что только с помощью математики можно писать алгоритмы, это привносит ущербность в программирование.
Ну и потом. Когда в математике написано x=3 — это что по вашему?
Сравнение, подстановку и присваивание путают только очень начинающие программисты. Почитайте, что это.
фикция, разницы между подстановкой и присваиванием нет, просто разная терминология математиков и программистов. даже смешно понаблюдать как вы сделаете подстановку без присваивания.
Не встречал в математике подстановку в «теле» функции. В программировании — сплошь и рядом.
Так вот функция в программировании это процесс рассуждения/доказательства/и т.п. в математике. А в этом процессе вы не раз подставляете для решения ну хотя бы системы уравнения.

И программирование к математике не сводится!
Вот именно, что подставляю. Один раз на итерацию. В программировании сплошь и рядом меняется значение во время итерации. Хотя бы счёт итераций :)
разница в том, что считать итерацией. Итерацией вообще-то считается машинная операция.
Это у кого она так считается? Лично у меня это, прежде всего, проход тела цикла.
Совершенно не естественно, скажем математически описывать действия по открытию счета в банке, или описывать заполнение платежа.
Должны выполняться математические тождества типа «сумма, внесённая в кассу, тождественно равна сумме, зачисленной на счёт».
И что, должны. И думаете Вы с помощью таких ограничений опишите все действия нужные для открытия счета?
Вы что-нибудь про бета-редукцию слышали, и вообще — про лябмда-исчисление в целом? Вряд ли, иначе бы разница между присваиванием и подстановкой была бы вам известна.
Да, я не спорю, что функциональщики математически подсуетились — только вот какое это отношение имеет к программированию ;)
Вы, очевидно, самоучка, при чем некачественный. Потому что в университетах рассказывают, какая связь между программированием и лямбда-исчислением. В общем, идите, изучайте основы, да и вот вам Lakret уже ссылок на нужные вещи накидал. После этого с вами о чем-то можно будет еще говорить.

Лямбда-исчисление
начинается, когда нет аргументов, переход на личности. Проходили знаем :)
С какой стати я буду приводить вам аргументы, если вы сейчас своей лже-наукой на любой аргумент всякую чушь пишете, не совместимую не только с логикой, но и наукой настоящей, например, Computer Science, математика? Сказано вам: идите и подтягивайте свои знания, потому что пока здесь у вас — сплошное невежество.
Единственная цель этой редукции может быть только в ускорении расчетов. Но тут выясняется, что как раз этого то и нет в функциональных языках. И теперь подумайте и ответьте почему?
Что такое бета-редукция, о которой вы сейчас так смело рассуждаете, раскажите пожалуйста?
Редуцируйте это выражение, например:
(\x. y) ((\x. x x) (\x. x x))
Имеет ли оно нормальную форму? Какой порядок редукции вы бы использовали, чтобы найти её?
Что такое call-by-value, call-by-need и call-by-name?
Подумайте, насколько вы знакомы с теорией computer science? Может, таки стоит начать учиться?
Ради чего? Чтобы меня спрашивали — зачем ты это изучал, и я как вы не знал что ответить?
Потому что здесь уже не только я выражаю сомнения по поводу вашей квалификации.
Мозги прочищает по любому. По крайней мере понимаешь, что процедурный подход (пускай и в ООП обёртке) не идинственный. И даже с удивлением замечаешь, что какие-то возможности ФП использовал ещё лет 20 назад, например, передавая в функцию указатель на функцию.
*единственный
Ага, читаю как раз Паттерны Command и Strategy с точки зрения функционального программирования

А вот ФВП, обрабатывающая исключения:

private static void ExecuteErrorProneCode(Action procedure)
{
try
{
procedure(); //исполняем переданную в качестве параметра функцию
}



и удивляюсь, с каких пор использование событий стало ФВП :) весело.
О каких событиях вы сейчас говорите? Я знаю только такие, и я их здесь не вижу.
Использование указателей на функцию — это один из методов реализации ФВП. Сами же функции высшего порядка появились задолго до указателей и компьютеров — вспомните оператор дифференцирования, например.
На этом я прекращаю дискуссию с вами, потому что ваша базовая необразованность делает её заведомо контрпродуктивной.
Ну, конечно математика появилась раньше :) Только ФП тут причем…

Ой, ну и посмешили… и зачем мне ваши ФВП — мне указателей на функции, делегатов и событий — хватает. С более цивилизованной нотацией.

А терминологию можно перекручивать — это пожалуйста, сколько влезет… только ничего нового пока что от ФП.
Исполнение функций по указателю было задолго до функционального программирования, поэтому называть это функциональным программированием не серьезно. Это не есть признак ФП, это скорее общие место в ООП и ФП.
Либо определение области (множества) значений переменной (со словом «пусть» в начале), либо верное или неверное тождество. То есть переменные в математике-то есть, но они носят другой смысл. Это не память, а абстракция множества, захватывающая все допустимые значения одновременно. x2 = 9 по сути ничем не отличается от x=3 (только множество шире) в математике, но не имеет смысла в «классических» ЯП.
Уравнение.
1. Существует только логическая парадигма программирования.
2. Существует только функциональная парадигма программирования.
3. Не существует парадигм программирования (ОПП, структурной, функциональной, логической и т.д), т.к. ни одна полностью не удовлетворяет реальные запросы программиста.


Составив статью по вашей схеме, можно подтвердить любое из этих высказываний и долго рассуждать на околонаучные темы в комментариях. Правда, в этом около нуля пользы и смысла.
Речь то в статье не об этом, читали? Речь же не о «запросах программиста»…
Если думать только в терминах структур и объектов — то естественно, что это и будут 2 единственные парадигмы для императивного программирования. По степени тривиальности это, простите, как у британских ученых.
Другое дело, что задачи, которые приходится решать программистам весьма далеко могут выходить за пределы этих двух систем описания, либо, наоборот, ни разу не требовать местами переусложенных моментов от них.

В общем, провокация-то удалась, но лучше всего решает задачу то, что ее хорошо решает, а не «парадигмы», независимо от того, как громко, часто и красиво они звучат.
Чистые функции (вы же о них?) вполне себе ложатся на структурную парадигму — она не требует хранения состояния. Да и на ООП, если исключить мутабельные объекты.
В общем случае — нет, не только про функциональное программирование. Задачи ИИ (в особенности слабого, наподобие распознавания образов), логистика, модели (не путать с «моделированием», которое «симуляция»), задачи принятия решений — их, естественно, можно свести до состояния объектов, но это будет не решение задачи, это будет костыль для него.
Важно: т.к. языки, использующие ООП (java, с++, c# — как наиболее актуальные и очевидные примеры) являются тюринг-полными, то с помощью них можно решить любую задачу, которая в принципе представляется решаемой. Но нужно различать «решение» от «языка». В вышеупомянутых случаях язык будет только промежуточным средством для построения действительного решения.

В общем, не даже если кто-то хорошо владеет «молотком» (ООП), не нужно во всем видеть «гвозди», только потому, что это удобней. Нужно решать задачу, а не городить костыли.
Не понимаю почему это будет костылём. Какая разница передам я в функцию вычисления оптимального пути множество координат, представляющее собой граф, или объект этот же граф и представляющий.

Да и вообще, структурное программирование и ООП я рассматриваю как организацию кода прежде всего. В первом мы выделяем код в структуры, во втором в классы и объекты. Уровне ниже мы можем применять хоть процедурный, хоть функциональный подход, хоть их комбинацию. На одних языках проще первый, на других — второй, на третьих — и комбинация выглядит естественно. А инкапсулируя реализацию нам вообще всё равно, что творится в методе, то ли там цикл, то ли рекурсия. Главное чтобы побочных эффектов не было либо они были задекларированы.
Тут начался холивар, за функциональное программирование. Хотя статья несколько о другом.

Я задам лишь единственный вопрос: из-за чего я должен отказываться от объектной модели, что взамен мне может дать функциональное программирование? Пока не одного четкого ответа не было.
Вижу — ответов нет, одни эмоции.
А отказываться не обязательно. Многие языки сочетают эти подходы. Как вы сами писали, ООП более высокоуровневая парадигма, чем функциональное или процедурное программирование ;)

Можно и на ООП-языках писать, не нарушая принципов ФП, а можно на них же выдавать простыни «спагетти» вообще ничему не соотвествующие кроме парадигме «говнокод».
Вот и я о том же!
«Можно и на ООП-языках писать, не нарушая принципов ФП»

только вот так не получится. Как только я объявляю переменную — я нарушаю ФП :)
Так не объявляйте переменные :) Используйте только параметры.
Вот и есть бредовая идея. И не понятно ради чего.
Да, и не потому, что я не читал про ФП, просто их рассуждения мне с самого начала бредом кажутся.
Уменьшение связанности, например.
Вот как я повышу связность объявив локальную переменную?
Нет, наверно я не добьюсь сегодня ответа. Не ужели так сложно ответить, если это так хорошо, в чем достоинства функционального программирования для программиста?
Итак, тема функционального программирования закрыта. Я убедился, что аргументов о большой пользе сего нету, работает это медленно (пример вам реализация перцептрона, см. статью). Другое даже назвать оппоненты назвать словами не могут, одни эмоции. Спасибо все.
А и да, холивар о чистоте функций — оставьте математикам, мне же программировать надо, а не болтологией заниматься.
В вот тут вообще офигиваю

Многие современные языки поддерживают ФВП в той или иной степени. Например, в C# есть механизм делегатов.

С каких пор делегаты стали функциональным стилем? Вы хоть знаете откуда они произошли?
ладно, понял… ФП используют также как и ООП понятие ссылки на функцию. А отличия то есть?
или только умной терминологии с математики понабрались и все?
И вот что характерно — минусуем, а не одного ответа? что господа хорошие нечего сказать, аргументов нет?
Спрячьте обратно. Заминусуют же.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации