А в вашем языке программирования так можно?

Полезные статьи и переводы интересных статей
tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

А в вашем языке программирования так можно?

Сообщение tyomitch » 06.08.2006 (Вс) 15:01

А в вашем языке программирования так можно?

оригинал: Joel on Software, 1.8.2006



Однажды, когда вы просматриваете свой код, вы обнаруживаете в нём два больших блока, содержимое которых практически не отличается. Они полностью совпадают – за исключением того, что в одном из блоков используются “Спагетти”, а в другом – “Шоколадный Мусс”.

Код: Выделить всё
// Самый простой пример:

alert("Мне Спагетти!");
alert("Мне Шоколадный Мусс!");

(Здесь для примеров кода используется JavaScript, но даже если вы не знаете этот язык, это вам не помешает понять суть.)

Естественно, что этот повторяющийся код лучше вынести в отдельную функцию:

Код: Выделить всё
function SwedishChef( food )
{
    alert("Мне " + food + "!");
}

SwedishChef("Спагетти");
SwedishChef("Шоколадный Мусс");

Это, конечно, очень простой пример, но в более реалистичных примерах применимы все те же рассуждения. Исправленный код лучше исходного в силу многих причин, каждую из которых вы уже слышали миллион раз. Удобство чтения, удобство поддержки, инкапсуляция, – одним словом, красота!

Теперь вы замечаете два других похожих блока кода: разница между ними лишь в том, что один вызывает функцию BoomBoom, а другой – функцию PutInPot. В остальном же эти блоки совпадают.

Код: Выделить всё
    alert("взять омара");
    PutInPot("омара");
    PutInPot("воду");

    alert("взять курицу");
    BoomBoom("курицу");
    BoomBoom("кокос");

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

Код: Выделить всё
function Cook( i1, i2, f )
{
    alert("взять " + i1);
    f(i1);
    f(i2);
}

Cook( "омара", "воду", PutInPot );
Cook( "курицу", "кокос", BoomBoom );

Смотрите-ка! Одним из аргументов мы передаём функцию.

А в вашем языке так можно?

Так… Теперь предположим, что функции PutInPot и BoomBoom у нас ещё не определены. Не правда ли, было бы весьма удобно просто записать их код в качестве аргумента – вместо того, чтобы объявлять их где-нибудь в другом месте?

Код: Выделить всё
Cook( "омара",
      "воду",
      function(x) { alert("положить " + x); }  );
Cook( "курицу",
      "кокос",
      function(x) { alert("стукнуть " + x); } );

Обалдеть как здорово. Я могу создавать функцию прямо там, где она мне нужна, – мне даже не нужно выдумывать для неё имя. Я просто беру её за уши и засовываю в другую функцию.

Как только вы привыкаете использовать в качестве аргументов анонимные функции, вы сразу же обнаруживаете по всей программе фрагменты кода, которые, например, делают что-то со всеми элементами массива:

Код: Выделить всё
var a = [1,2,3];

for (i=0; i<a.length; i++)
{
    a[i] = a[i] * 2;
}

for (i=0; i<a.length; i++)
{
    alert(a[i]);
}

Делать одно и то же со всеми элементами массива – настолько часто встречающаяся задача, что вы решаете завести для неё отдельную функцию:

Код: Выделить всё
function map(fn, a)
{
    for (i = 0; i < a.length; i++)
    {
        a[i] = fn(a[i]);
    }
}

Теперь приведённый выше код можно записать гораздо проще:

Код: Выделить всё
map( function(x){return x*2;}, a );
map( alert, a );

Другая часто встречающаяся задача – объединить все элементы массива некой агрегирующей функцией:

Код: Выделить всё
function sum(a)
{
    var s = 0;
    for (i = 0; i < a.length; i++)
        s += a[i];
    return s;
}

function join(a)
{
    var s = "";
    for (i = 0; i < a.length; i++)
        s += a[i];
    return s;
}

alert(sum([1,2,3]));
alert(join(["a","b","c"]));

Функции sum и join настолько похожи, что вам кажется естественным вынести их суть в функцию общего назначения, объединяющую все элементы массива в одно значение:

Код: Выделить всё
function reduce(fn, a, init)
{
    var s = init;
    for (i = 0; i < a.length; i++)
        s = fn( s, a[i] );
    return s;
}

function sum(a)
{
    return reduce( function(a, b){ return a + b; },
                   a, 0 );
}

function join(a)
{
    return reduce( function(a, b){ return a + b; },
                   a, "" );
}

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

В Java для того, чтобы работать с функцией как с отдельной сущностью, приходится создавать целый класс с единственным методом – такой класс называется “функтор”. Прибавьте к этому то, что во многих ОО-языках нужно создавать целый файл под каждый объявляемый класс – и программа моментально становится по-настоящему неуклюжей. Если в вашем языке программирования для работы с функциями приходится использовать функторы, значит вы отстаёте от достижений современных технологий программирования. Можете попытаться втолковать это тому, кто призывает вас использовать этот язык.

На самом деле, насколько велик выигрыш от вынесения в отдельную функцию крошечного блока кода, который только и делает, что перебирает все элементы массива?

Вновь рассмотрим нашу функцию map. Когда вам нужно сделать одно и то же со всеми элементами массива по очереди, как правило, вам совершенно не важно, в каком порядке будут перебираться его элементы. Ваш результат ведь не изменится от того, что функция будет пробегать по массиву задом наперёд, – так? Если у вас под рукой окажутся два процессора, то можно будет даже реализовать map так, чтобы каждый процессор обрабатывал половину массива – и сразу же всюду в вашей программе, где раньше был цикл по всем элементам массива, производительность увеличится вдвое.

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

В этом случае можно будет, скажем, написать невероятно быстрый код для поиска строки по всем Интернет-страницам. Это будет просто вызов map, аргументом которой передаётся обычная функция поиска подстроки в строке.

Но самое любопытное здесь то, что как только функции map и reduce становятся общедоступными, и как только ими начинает пользоваться большая группа людей, достаточно лишь одного супер-гения, который приспособит их для выполнения на глобальной сети параллельных вычислителей, – и сразу же весь старый код, использующий эти функции, будет выполняться в миллионы раз быстрее, чем с обычным циклом по массиву; теперь в одно мгновение можно будет решать задачи, требовавшие раньше месяца вычислений.

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

Теперь-то вы понимаете, что я имел в виду, когда я сетовал на преподавание студентам КН одной лишь Java:
Без представления о функциональном программировании невозможно изобрести MapReduce – алгоритм, обеспечивающий такую ошеломляющую масштабируемость Google. Сами термины Map и Reduce происходят из языка Lisp и традиций ФП. Сам же алгоритм MapReduce очевиден любому, кто помнит из курса по программированию, что чисто функциональные программы не имеют побочных эффектов и потому тривиально масштабируются в параллельных системах. То, что в Google догадались до MapReduce, а в Microsoft – нет, уже объясняет, почему в Microsoft всё никак не доделают нормальную поисковую систему, а в Google уже перешли к следующей задаче – создание Скайнета самого крупного параллельного суперкомпьютера в мире. Я не уверен, что в Microsoft по-настоящему понимают, насколько они отстали.

Теперь, надеюсь, я вас убедил, что языки программирования, поддерживающие функции как самостоятельные сущности, предоставляют большие возможности для инкапсуляции, – что, в свою очередь, делает ваш код более простым, компактным и масштабируемым. Многие программы Google используют алгоритм MapReduce, и они все оказываются в выигрыше, когда кто-то оптимизирует его код или исправляет в нём баги.

Лично я считаю, что самые продуктивные языки программирования – те, которые позволяют работать с разными уровнями абстракции. В GW-BASIC вообще нельзя было создавать функции. (прим. перев.: а как же DEF FN?) В Си есть указатели на функции, но они жуууткие, не анонимные, и обязательно должны быть объявлены где-нибудь совсем не в том месте, где они используются. В Java приходится использовать функторы, что ещё жутче: как рассказывает Стив Йегг, Java – Царство Имён.
Изображение

Q2W
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 745
Зарегистрирован: 31.01.2004 (Сб) 20:46
Откуда: Питер

Сообщение Q2W » 06.08.2006 (Вс) 17:19

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

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 06.08.2006 (Вс) 17:59

Q2W, про ФП в Перле я уже писал: http://bbs.vbstreets.ru/viewtopic.php?t=26320
Изображение

Q2W
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 745
Зарегистрирован: 31.01.2004 (Сб) 20:46
Откуда: Питер

Сообщение Q2W » 06.08.2006 (Вс) 18:03

tyomitch писал(а):Q2W, про ФП в Перле я уже писал: http://bbs.vbstreets.ru/viewtopic.php?t=26320

Я просто ответил на вопрос, заданный в сабже.
Я знаю верный путь

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 06.08.2006 (Вс) 18:10

Ну, тогда дополню, что по слухам в .net 2.0 (конкретно в C# и JS.net) тоже так можно. Сам всё никак не поставлю VS2005, чтобы попробовать.

В VB2005 таких возможностей вроде бы нет, и не планируются.
Изображение

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 07.08.2006 (Пн) 2:47

Полагаю, нет нужды говорить, что одна маленькая фича под названием dynamic SQL позволяет в принципе реализовать такое, что Брилю разве что снилось :D.

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

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 07.08.2006 (Пн) 6:28

C# не скриптовый язык, а там так тоже можно :-P
Изображение

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 07.08.2006 (Пн) 11:47

В .NET можно и код компилить в рантайме и передавать на него управление... Так что, если немного поколдовать, то и в тамошнем VB можно реализовать нечто подобное.
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 07.08.2006 (Пн) 14:48

Дело не в том, чтобы это можно было реализовать это при помощи лома и такой-то матери, а чтобы это было так же просто и естественно, как в нормальных языках. Иначе просто теряется смысл.

Плюс, замыкания в Перле, C# и др. -- это не компиляция кода в рантайме. Сам код замыканий как раз компилируется вместе со всем остальным кодом.

О том, насколько всё будет тормозить, если каждый раз, когда одна из внешних переменных замыкания изменилась, компилировать его код из строки по-новой, -- не стоит и говорить.
Изображение

Ramzes
Скромный человек
Скромный человек
Аватара пользователя
 
Сообщения: 5004
Зарегистрирован: 12.04.2003 (Сб) 11:59
Откуда: Из гробницы :)

Сообщение Ramzes » 07.08.2006 (Пн) 15:39

tyomitch
Компилируешь сборку и с ней работаеешь (Reflection) если чего-то поменялось, еще раз компилируешь. :roll: Можно на VB 2005(3), C#

Konst_One
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
Аватара пользователя
 
Сообщения: 3041
Зарегистрирован: 09.04.2004 (Пт) 13:47
Откуда: Химки

Сообщение Konst_One » 07.08.2006 (Пн) 16:15

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


Это жуть совсем не простая и не компактная и не удобочитаемая, ИМХО
:shock:

Денис Победря
Мегобойанист
Мегобойанист
 
Сообщения: 1037
Зарегистрирован: 03.01.2005 (Пн) 21:29
Откуда: Из Москвы

Сообщение Денис Победря » 07.08.2006 (Пн) 17:19

Сорри за флуд, но в Смолтолке это обычное дело.
[Место cдаётся]

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 07.08.2006 (Пн) 18:20

Ramzes писал(а):tyomitch
Компилируешь сборку и с ней работаеешь (Reflection) если чего-то поменялось, еще раз компилируешь. :roll: Можно на VB 2005(3), C#

Вот-вот. "Вы переместили указатель мыши. Чтобы изменения вступили в силу, нужно перезагрузить компьютер." (с)
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 17.11.2006 (Пт) 11:43

Оказывается, я зря старался: у этой статьи уже был [url=http://local.joelonsoftware.com/mediawiki/index.php/А_ваш_язык_программирования_так_может%3F]перевод на офф. сайте[/url].
Я опоздал всего на 3 дня.

Ладно, зато теперь у всех есть возможность сравнить качество перевода :-)
Открыть под это дело холивор? :lol:
Изображение

Ramzes
Скромный человек
Скромный человек
Аватара пользователя
 
Сообщения: 5004
Зарегистрирован: 12.04.2003 (Сб) 11:59
Откуда: Из гробницы :)

Сообщение Ramzes » 17.11.2006 (Пт) 15:33

tyomitch писал(а):Ладно, зато теперь у всех есть возможность сравнить качество перевода :-)
Открыть под это дело холивор? :lol:


сказано сделано
http://bbs.vbstreets.ru/viewtopic.php?t=29275

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 27.11.2007 (Вт) 9:54

Нашёл блог, в котором кроме бэшинга американской демократии и тому подобного мусора попадаются занятные любительски-лингвистические заметки. Например, с уже затронутой темой "Java -- царство имён" пересекаются вот эти две, сравнивающие веб-сервисные протоколы и универсалии естесственных языков.

Сам блог, судя по всему, заброшен, но в его архивах осталось достаточно интересных вещей. Просто очень долго их выискивать среди рэнтов.


(В своём разделе лень открывать тему под этот пост, поэтому пощу здесь.)
Изображение


Вернуться в VBStreets Knowledge Base

Кто сейчас на конференции

Сейчас этот форум просматривают: AhrefsBot и гости: 0

    TopList