Pull to refresh

Comments 244

Спасибо Джоэлу за классные мысли.
Спасибо Гвидо за приличную реализацию.
Про PHP5.3 вспоминать неспортивно, большинство кода из поста можно переписать на PHP3 :)
Можно, хоть и выглядеть будет не так красиво.
function Cook($ingredientOne, $ingredientTwo, $function)
{
    echo "get the $ingredientOne";
    $function($ingredientOne);
    $function($ingredientTwo);
}

Cook(“lobster”, “water”, "PutInPot");
Cook(“chicken”, “coconut”, "BoomBoom");


Разве? :)
Я про create_function и код в строке. А вообще PHP3 — далёкое прошлое. Хватит его ворошить.
Новый виток войны против гибридизации парадигм объявляю открытым.
Не понял, о чем статья?
А вообще забавно, поливать грязью Microsoft, привозносить Google, но при этом пользоваться C# :)
Начал чтение с оригинала, и не пожалел об этом. Спольски пишет грамотно (sic!), примеры приводит на JavaScript; вместо «З.Ы.» про GW-BASIC у него стоит «Correction», или, по-нашему «UPD».
Мой язык умеет это делать, и умел 20 лет назад.
Сравнение с первоисточником наглядно показывает, что на джаваскрипте все эти примеры выглядят проще, чем на C#.
да ладно. Потому что у параметров функций не надо писать типы? На мой взгляд так почти идентичные примеры как раз.
Та не, зря на С# переписали. Стало гараздо хуже.
Потому что в C#, в отличие от браузерного джаваскрипта:

  • у параметров функций надобно указывать типы;
     
  • у возвращаемого функцией значения надобно указывать тип;
     
  • если функция статическая (static) и доступная (public), то это также надобно указывать;
     
  • приходится использовать Console.WriteLine(string.Format("…", …)) вместо alert() и строковой конкатенации;
     
  • надобно указывать тип массива (new List<int> { 1, 2, 3 } или new[] { «a», «b», «c» }) вместо того, чтобы просто перечислить ряд элементов в квадратных скобках.

А на браузерном джаваскрипте со всем этим не приходится возёхаться.

Правда, если джаваскрипт не браузерный, а нодовый (Node), то возёхаться приходиться чуть больше (нет alert(), есть console.log()), однако всё ещё далеко не столько ненужной возни, сколько в C#.
Вы только что перечислили все то, что мне не нравится в джава-скрипте =)

Не вижу никаких проблем с «new List { 1, 2, 3 }» и с тем, чтобы указать static или public

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

если функция статическая (static) и доступная (public), то это также надобно указывать;
Это плата за ООП. ООП действительно не нужно в маленьких приложениях, но C# — язык для чуть более серьезных дел.

приходится использовать Console.WriteLine(string.Format("…", …)) вместо alert() и строковой конкатенации;
Строковая конкатенация до сих пор работает: Console.WriteLine("a * b = " + a * b). И потом, string.Format у вас лишний. Console.WriteLine("a * b = {0:0.00}", a * b) — автоматически использует string.Format. По поводу того, что нельзя никак (даже с помощью спец.оператора) использовать WriteLine вместо Console.WriteLine — ну да, немного фэйл в языке, хотя это не так уж и плохо, тем более учитывая наличие IntelliSense это вообще не проблема.
Это плата за статическую типизацию и более быструю работу приложения.
Автоматический вывод типов? Нет, не слышал…
Слышал, слышал. На данный момент в C# не используется автоматический вывод типов для результатов функции и её параметров — это еще не реализовано, ибо требует усилий, а полезность немного сомнительна, хотя это выглядит клево. Но мы же сравниваем JS и C#, а не C# и Haskell.
Можно сравнить Scala и C#? :)
Вы пишете: «необходимость указывать типы параметров функций и возвращаемых значений — это плата за статическую типизацию и более быструю работу приложения». Я говорю — нет, это никак не связанные вещи, т.к. есть статически типизированные языки, этого не требующие (с автоматическим выводом типов). И _неважно_, какие языки вы сравниваете: от того, что в одном языке есть A (отсутствие необходимости явно задавать типы) и нет B (статической типизации), а во втором языке — наоборот, _никак_ не следует, что A и B связаны причинно-следственной связью.
это точно, на C# примеры очень «шумные»
на JS — чистые и опрятные
на питоне еще бы опрятнее были бы
к сожалению, не знаю этого языка, продемонстрируете?
но вам в это случае все равно прийдется определить такую функцию где-то

Вот он, язык, который я хочу — в котором для использования функции не обязательно ее определять!
И все-таки на JavaScript это выглядит намного изящнее
Угу. По результатам беглого просмотра примеров, заключаю, что C# все-таки не совсем «может делать такое» — слишком много ключевых слов нужно запомнить для доступа к базовым возможностям языка.
Разница в том, что C# — язык широкого назначения со статической типизацией с туевой кучей возможностей, а JavaScript — это встраиваемый язык с динамической типизацией. Тип типизации решает все — количество ключевых слов (их совсем немного, кстати) и скорость работы приложения.
Мне не нравится когда говорят «в старых языках этого нет а в новых есть». Всё это было еще задолго до этих ваших старых языков. Правильнее было бы говорить либо «как обычно мода пошла по кругу и возвращается к 60-м годам 20-го века» либо «наконец-то разрабы мейнстрим языков одумались и начали поддерживать столь многими желанные с 60-х годов вещи»
JavaScript может.
Вообще мне после JS огромное количество языков (типа C++) кажутся ужасно неуклюжими. Как жить, если нельзя передать в функцию аргументом другую функцию (анонимную)?

В этом плане также есть Python (а может и Ruby, не знаю), который позволяет делать такие же вещи.
UFO just landed and posted this here
Он не неуклюж, просто мне кажется таковым. После JS или Python.

Например, статическая типизация. Я понимаю, зачем это, как… Но всё равно утиная удобнее — хоть и медленнее. И даже auto (которая auto x = 1 вместо int x = 1) не спасает.
Утиная в плюсах тоже есть — на шаблонах. Да, это самая настоящая утиная типизация — от типа требуется только то, что вы просите от его объектов и не более того.
А уж как javascript после java неуклюж, когда в переменной со строкой внезапно оказывается массив строк.
Это не массив строк. Строка — это массиво-подобный объект. Со свойством length и индексами.
'abc'[1]; // 'b'
'abc'[0][0][0][0][0][0]; // 'a'
Вы считаете это минусом?
Я совсем не это имел ввиду, а то, что из за динамической типизации никода не знаешь, что находится в переменной. Особенно если код писал какой-нибудь юный падаван.
Это может быть и плюсом. Если сравнивать JavaScript и Java, то в первую очередь бросается в глаза тяжеловесность и многословность последней. Мне в Java порой очень не хватает легкости функционального стиля, который есть в JS.

А проблемы, которые вы описали, решаются хорошим Code Convention.
А, ну тогда есть typeof. Или {}.toString.call(object)
В этом плане есть любой функциональный язык программирования, где функции являются объектами первого порядка :)
Java требует от вас создания целого объекта с одним методом, называемым функтором, если вы вдруг захотите использовать функции как объекты первого класса. А принимая во внимание что в большинстве ОО языков заведено для каждого класса создавать отдельный файл, все очень быстро становится крайне неуюклюжим.

А вот и неправда. В Java довольно давно есть локальные классы и анонимные классы, причём они даже вытаскивают переменные из объемлющей области видимости. Конечно, это не верх выразительности и что-то вроде \a \b -> a + 2 * b там не напишешь. Но лично я очень даже не брезгую определять анонимные классы при необходимости. Кстати, местами оно иногда даже лучше лямбд получается. Взять тот же случай обработки результата асинхронного вызова. В javascript это три разные функции — success, failure и done. В Java это один класс. Если, например, в success или failure вычисляются какие-то данные, нужные для done, то с анонимным классом меньше геморроя, чем с тремя лямбдами.
После Perl все эти восхищения JavaScript воспринимаются с удивлением.
<сарказм уровень="88%">

Ну да, как же можно не восхищаться языком, на котором возможна программа типа такой?

#!/usr/bin/perl -w
use strict;

                                           $_='ev
                                       al("seek\040D
           ATA,0,                  0;");foreach(1..3)
       {<DATA>;}my               @camel1hump;my$camel;
  my$Camel  ;while(             <DATA>){$_=sprintf("%-6
9s",$_);my@dromedary           1=split(//);if(defined($
_=<DATA>)){@camel1hum        p=split(//);}while(@dromeda
 ry1){my$camel1hump=0      ;my$CAMEL=3;if(defined($_=shif
        t(@dromedary1    ))&&/\S/){$camel1hump+=1<<$CAMEL;}
       $CAMEL--;if(d   efined($_=shift(@dromedary1))&&/\S/){
      $camel1hump+=1  <<$CAMEL;}$CAMEL--;if(defined($_=shift(
     @camel1hump))&&/\S/){$camel1hump+=1<<$CAMEL;}$CAMEL--;if(
     defined($_=shift(@camel1hump))&&/\S/){$camel1hump+=1<<$CAME
     L;;}$camel.=(split(//,"\040..m`{/J\047\134}L^7FX"))[$camel1h
      ump];}$camel.="\n";}@camel1hump=split(/\n/,$camel);foreach(@
      camel1hump){chomp;$Camel=$_;y/LJF7\173\175`\047/\061\062\063\
      064\065\066\067\070/;y/12345678/JL7F\175\173\047`/;$_=reverse;
       print"$_\040$Camel\n";}foreach(@camel1hump){chomp;$Camel=$_;y
        /LJF7\173\175`\047/12345678/;y/12345678/JL7F\175\173\0 47`/;
         $_=reverse;print"\040$_$Camel\n";}';;s/\s*//g;;eval;   eval
           ("seek\040DATA,0,0;");undef$/;$_=<DATA>;s/\s*//g;(   );;s
             ;^.*_;;;map{eval"print\"$_\"";}/.{4}/g; __DATA__   \124
               \1   50\145\040\165\163\145\040\157\1 46\040\1  41\0
                    40\143\141  \155\145\1 54\040\1   51\155\  141
                    \147\145\0  40\151\156 \040\141    \163\16 3\
                     157\143\   151\141\16  4\151\1     57\156
                     \040\167  \151\164\1   50\040\      120\1
                     45\162\   154\040\15    1\163\      040\14
                     1\040\1   64\162\1      41\144       \145\
                     155\14    1\162\       153\04        0\157
                      \146\     040\11     7\047\         122\1
                      45\15      1\154\1  54\171          \040
                      \046\         012\101\16            3\16
                      3\15           7\143\15             1\14
                      1\16            4\145\163           \054
                     \040            \111\156\14         3\056
                    \040\         125\163\145\14         4\040\
                    167\1        51\164\1  50\0         40\160\
                  145\162                              \155\151
                \163\163                                \151\1
              57\156\056

Она к тому же в цвете на Хабрахабре выглядит ещё круче, чем в чёрно-белом исходнике на http://www.cpan.org/misc/japh.

</сарказм>
Пишу на Перле 12 лет и до сих пор не научился на нем писать! Очень интересный язык!
То же самое есть и на Си (программы в виде картинок), например, так в чём юмор?
«параллелизируемо» — это пять :)

по теме — Groovy и его замыкания, думаю, вполне подойдут.
Стало неинтересно читать после примера:
var list = new List<int> { 1, 2, 3 };

for (var i = 0; i < list.Count; i++)
{
     list[i] = list[i] * 2;
}

foreach (var el in list)
{
    Alert(el.ToString());
}



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

foreach (var el in Enumeration.Range(1, 3).Select(i => i*2))
{
    Alert(el.ToString());
}


или если в одну строку:

Enumeration.Range(1, 3).Select(i => i*2).ToList().ForEach(el => Alert(el.ToString()));


Так по крайней мере не теряется понимание, что же было задумано сделать изначально.

Вообще я обожаю LINQ — он позволяет писать запросы параллельно с придумыванием и даже направлять мысли в нужном направлении.
UFO just landed and posted this here
Хм, забавно писать про функциональные вещи на C#, последний пример выглядит гораздо элегантней на `функцональных от рождения` языках, например на ocaml этот кусок кода:
public static int Sum(IEnumerable<int> list)
{
    return Reduce((x, y) => x + y, list, 0);
}

public static string Join(IEnumerable<string> list)
{
    return Reduce((a,b) => a + b, list, string.Empty);
}

выглядел бы так:
sum = foldl1 (+)
join = foldl1 (^)
UFO just landed and posted this here
На Ruby можно проще:
(1..5).inject(:+)
UFO just landed and posted this here
Любой язык программирования:
UFO just landed and posted this here
В обратной польской записи еще проще:
5 1 5 + * 2 /
module Enumerable
  def sum
    inject(:+)
  end
end

(1..5).sum
UFO just landed and posted this here
Вы же понимаете, что не в Range дело, а в .inject(:+). Для таких простых арифметических операций гораздо приятнее использовать символ, чем неуклюжий блок.
На шарпе это вопрос легковесных тулкитов. Для того, чтобы писать типизировано, безопасно и контролируемого еще на этапе компиляции так: new int[] {1,2,3,4,5}.inject((sum,x) => sum += x), что очень похоже, достаточно единожы написать такую функцию:
    public static class Extensions
    {
        public static T inject<T>(this T[] array, Func<T, T, T> func)
        {
            T result = default(T);
            foreach (T item in array) result = func(result, item);
            return result;
        }
    }

Зачем писать самому метод inject если есть уже готовый Aggregate ?
Поскольку топик о фичах языков программирования, а не о библиотеках, я решил попутно показать такую возможность как extension method. Как подобные inject'ы могут появляться даже если для них нет готового решения в классе Enumerable.
sum = list.Sum();
join = string.Join(list, /*separator: */ ", ");
И еще один C# пример:
Console.WriteLine(list.Aggregate("", (s, element) => s + element));
Console.WriteLine(list.Aggregate(0 , (s, element) => s + element));

Мне лично название Aggregate более понятно чем foldl1 (почему l1? ладно, почему 1?), ну да ладно, специфика веселого именования в функциональных языках меня всегда удивляла.
fold — свертка
l — left
1 — инициализирующим элементом выступает первый элемент списка
Ну почему не foldLeft1 хотя бы? А что, есть еще foldl0, foldl2, ..., foldl100?
Зачем foldl2? У тебя есть два случая:
— список может быть пустым, нужен изначальный элемент
— изначального элемента нет, берем первый, но если список пустой — падаем

По поводу кажущейся излишней краткости: нужно понимать что fold — это почти как foreach в C# по частоте использования. Никто же не говорит почему foreach не заставляют писать в camel-case (forEach)?

Т.е. там все толково продумано: отлично выбранные имена, хорошо согласованная, не раздутая, система суффиксов (там еще буквально 2-3 есть таких).

Вообще стандартная библиотека Хаскеля — это почти шедевр.
www.haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html
И тут скорее дело не в названиях а ток как в функциональных языках можно комбинировать фунции, например большинство функций которые принимаю функции как аргумент скорее всего принимают их как первый аргумент и это позволяет одно строчкой делать новую функцию просто скомбинировав.
А также помогает то что + это функция а не оператор.
На C# можно было бы просто вызвать метод Aggregate
Может быть я сильно ошибаюсь. Может быть так делать неверно. Но я использую подобный сахар только если отчетливо понимаю, что другой человек сразу поймет мой код.
Потому что это единственная гарантия того, что я сам его пойму через пол года.
Очень многое из написанного выше на самом деле укорачивает код на несколько строчек и вероятнее делает его красивее, но чрезвычайно ухудшает уровень понимания происходящего.

Конечно, много где эти возможности оправданы. Но очень часто можно столкнуться с тем, что их пихают просто потому что это клево выглядит.
Нет, вы конечно правы, что новый синтасический сахар это вредно, но проблема в том, что это а) не новый сахар б) это даже не сахар, это вещь, которую должен знать любой более-менее адекватный программист, если у него было время на обучение.
По факту, все что в статье описано — это умение абстрагироваться чуть-чуть на уровень выше + капельку про MapReduce, про который все (даже студенты) хотя-бы слышали краем уха (пусть и не разбирали)
в оригинале FORTRAN Apparently it got functions., а у вас "Вероятно, в нем появились функции."

поправьте на «По-видимому, в нём были функции».
Этот перевод был ранее.
«Анонимные функции» — противоречат ООП, поэтому их следует запретить. Это так по функциональному размывает четкость кода :) Функции — надо декларировать, описывая их вход, выход. Анонимные функции позволяют этого не делать, но это и превращает код в спагетти. В тексте уже не видите какими реально функциями вы пользуетесь, и дублируете их на уровне кусков кода вызова анонимных функций. Кто их использует — сможете вы гарантировать что только один раз на проект создали уникальную анонимную функцию. А что вы будите делать, если код анонимной функции надо вызвать в разных местах. Продублируете? Поздравляю…
Скажите, Smalltalk — объектный язык? (На всякий случай — в нём абсолютно _всё_ является объектами) А ведь в нём есть анонимные функции — правда, называются они «блоки». И (сюрприз) — они тоже являются объектами.
> Обратите внимание, что я создаю здесь метод на лету, и даже не заботясь о его имени, просто тащу зауши внутрь метода Cook()… Как только вы стали мыслить терминами “Анонимные функции в качестве аргументов метода”

Как только вы стали мыслить терминами “Анонимные функции в качестве аргументов метода” — Вы сразу начали писать спагетти-код. Не заботясь о имени метода, вы перестали заботится о повторном использовании и спецификации поведения объекта.
Возьмем такой безобидный пример «спагетти-кода»:
persons
  .Where(person => person.Age > 18 && person.Gender == Gender.Female)
  .Select(person => person.Address)


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

var address = from person in persons
              where person.Age > 18 && person.Gender == Gender.Female
              select person.Address;

Ну это шутки ради :)
Ну вы же сами понимаете, что этот синтаксис ни что иное, как синтаксический сахар к вышеуказанному примеру и выражение
person.Age > 18 && person.Gender == Gender.Female
всё-равно можно трактовать как анонимную функцию.
Это надо писать на SQL, а не тянуть в C# приложение.
Отличная идея кстати. В приложении без базы (или без SQL-базы) прикрутить SQL-базу и общаться с ней :)
Может лучше наоборот, обернуть базу так чтобы общаться с ней как с массивами, коллекциями и т. п.?
забыл добавить тег «сарказм». собственно Linq как бы для этого и используется, чтобы работать абсолютно одинаково как с коллекциями, так и с базой. Причем без потерь эффективности.
Как я понимаю, он работает с коллекциями как с базами, а я говорил о противоположном. Или опять сарказм не разглядел?
Он со всем работает одинаково, в декларативном стиле. Получить императивное описание по декларативному можно. А вот обратное вряд ли выйдет сделать. Вы написали, что хотите получить адрес людей, которые старше 18 и которые женщины, по этому описанию можно автоматически построить цикл или автоматически построить SQL запрос. А вот если я сразу напишу цикл, построить SQL будет невозможно, хрен его знает, что я так понаделал.
Против декларативного стиля ничего не имею. Не нравится sql-like синтаксис.
Громоздко в моём основном языке. :(
Это мой крест и мне его нести :)
Если приложение без базы, а Вы используете SQL — запросы — это клиника!
Если приложение без базы, а я пишу linq-запросы, которые похожи на sql, то это нормально. Хотя лично я предпочту лямбды.
Вот интересно, а где вы храните данные, чтобы обращаться к ним в стиле SQL?
Это может быть что угодно. Список системных шрифтов, из которых нужно выбрать те, у которых есть полужирное курсивное начертание, список директорий/файлов из которых нужно выбрать те, которые удовлетворяют некоторому условию, список процессов в диспетчере задач etc — т.е. все то, что совать в SQL-базу не нужно.
Для всего, что не нужно совать в SQL-базу — не нужно пользоваться SQL -запросами. Есть циклы, и отбор по условию…
Ну да, можно и так. Просто SQL-like-запросы и расширения IEnumerable позволяют держать все на одном уровне абстракции, а не перескакивать.
Сделайте мне пожалуйста 3 вещи, отбор людей старше 18, отбор женщин и отбор женщин старше 18. Предлагаете каждый раз цикл писать? А так я один метод написал, а критерии выборки могу снаружи подсунуть. Но про OCP вы видимо не слышали
sql-like это тоже будет неудобно, а вот лямбдами вообще замечательно
array_filter($array, $callback) из «соседнего» топика :)
Это то же самое и есть, если что) я предлагал через цикл сделать
Угу, но через цикл менее громоздко и быстрее получается. Замерял как-то.
это когда один цикл. а когда их три, а различаются они только ифом внутри?
Хм… В смысле передать в фильтр одну сложную лямбду, а не три простых последовательно? Надо будет поэкспериментировать. Не сравнивал отдельно время вызова функции и время итерации цикла, но разниа точно не на проценты была, разы, может даже порядок.
лямбды с помощью композиции функций можно комбинировать. естественно один обход со сложной комбинированной лямбдой будет эффективнее трех обходов с простыми.
Все зависит от того, где хранятся данные о людях. И я очень удивлюсь если это будет не база!
то есть если у вас хранится в SQL-базе, то метод один, если в коллекции, то другой? окей, у вас свое понимание красивого кода весьма расходящееся с мнением большинства.
В коллекции хранится не может.
на момент жизни приложения вполне может и в коллекции храниться. Или например кеш из той же базы данных.
Именно поэтому нужно везде использовать синтаксис коллекций, а SQL остаить на уровне непосредственно взаимодействующим с базой :)
А да, хранить их можно в чем угодно — массивах, списках, множествах, деревьях, skiplistах — в общем все, что позволяет хранить множество данных.
Не-а, тогда это «домострой», нет базы — нечего к ней обращаться SQL-ом
Ну и чем это не база данных? Данные хранит? Хранит. Обрабатывать позволяет? Позволяет. Фильтровать позволяет? Позволяет. Коллекции могут быть вложены и связаны? Могут. Просто коллекцию можно представить как урезанную базу данных и не SQL-базу. Но если это база данных, почему это нельзя к ней обращаться SQL-like запросами. И еще раз: это SQL-like, а не SQL-запросы
Персистентность не обеспечивает :) Другие словами, фигово хранит :)
Не имеет значения. Если нам не нужна персистентность (к примеру, мы пишем файловый менеджер, зачем нам хранить содержимое каталога такого-то, особенно если учесть, что он мог давно поменяться в своем содержимом), то используем коллекции + LINQ/Enumerable-расширения. Нужна — используем SQL.
Не нужна — используем коллекции, нужна, используем ORM, предоставляющую интерфейс коллекций.
Вообще говоря SQL это лишь структурированный (или структурный?) язык запросов, «внезапно» хорошо ложащийся на реляционные базы данных. Мои возражение относятся к его применению и в коллекциях, и в РСУБД (а вроде ещё и на нереляционные стали прикручивать :( )
в коллекциях, сравните
var address = from person in persons
              where person.Age > 18 && person.Gender == Gender.Female
              select person.Address;

c
var adress = new List<string>();
foreach(var person in persons)
{
  if(person.Age > 18 && erson.Gender == Gender.Female)
  {
     adress.Add(person.address)
  }
}


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

IEnumerable<string> getAddresses(Func<Person, bool> query)
{
return persons
  .Where(Query)
  .Select(person => person.Address);
}


adressesOfWomen = getAddresses(p =>p.Gender=Gender.Female);
adressesOfChildren = getAddresses(p => p.Age < 18);
//и так далее


Если что, то я только что описал фаулеровский QueryObject, только без мишуры классов и гораздо более гибкий. Применяя композицию функций можно просто потрясающие вещи вытворять.
Вариант с циклом замечателен, и наиболее ясен для понимания. А то, что там можно вклиниться — это благо для удобного расширения, опять же уже второй аргумент почему ФП не рассширяемо в отличии от ООП.
и как же там вклиниться интересно? у нас с вами разные понятия о расширяемости.
Вроде как раз возможность легкого вклинивания всегда была сильной стороной ФП. Мапперу/редусеру/фильтру передаём всегда функцию. Нужно что-то изменить — передаём другую функцию. С циклами тоже можно так поступать, но выглядит неестественно.

Плюс, хорошо если есть цикл типа foreach, скрадывающий детали итерирования, но если нужно писать что-то вроде for($i=0;$i<len($array);$i++) {//...} то либо заставляет задуматься лишний раз («имеет ли значение порядок и направление перебора»), либо может послужить причиной того, что не задумался когда нужно было, читая код — конструкция воспринимается как единое целое именно перебора с безразличным порядком, в детали не вдаёшься и решаешь оптимизировать скажем так for($i=len($array); $i; $i++) {//...} не обращая внимания на то, что порядок перебора был важен
Это не SQL-запросы, это SQL-like-запросы. Гуглите, что такое LINQ/интерфейс IEnumerable и его расширения. На самом деле отличная вещь. А в сообщении EvilBlueBeaver говорилось с помощью сарказма о том, что не нужно прикручивать sql-базу к приложению, которому эта sql-база не нужна. Отсутствие SQL-базы не мешает использовать SQL-like-запросы, ибо они могут проводиться над коллекциями.
Мне ничего гуглить не надо, это все известные для меня вещи. Я лишь идеологический противник этого бреда…
tac в своем репертуаре. Все, что он не понимает он считает бредом.
А какой из современных языков НЕ может этого?
Вот js бы я на вашем месте оставил, как-никак автор не зря выбрал его для выражения своих мыслей.
Не могу не согласится. C# был выбран скорее просто для того, чтобы не делать еще раз перевод, 1 в 1, как он уже был, наверняка не раз, сделан. Думаю что с тем же успехом можно было бы написать примеры на том же Питоне.
Правильно, уменьшим количество кода в угоду производительности. Очень опрометчиво смотреть на проблему однобоко, это может сказаться в будущем.
IMHO, читабельность и простоту кода нельзя приносить в жертву ради избежания копирования пары строчек.

Этот код хорош, потому что он простой:
Console.WriteLine(“get the lobster”);
PutInPot(“water”);
PutInPot(“lobster”);


И этот код смотрится хорошо:
Cook(“lobster”, “water”, PutInPot);

Но вариант с лямбда функциями просто ужасен:
Cook("lobster",
          "water",
          x => Alert("pot " + x));
Плюсую. Трехэтажные выражения вообще не стоит без особой надобности употреблять.
А я собственно о чем выше говорил ;) — но там мнения разделяются. Вот интересно почему?
а ужасен он потому, что плохо отформатирован, поэтому мозг ломается при чтении и ничего не понимает.

Cook(
    "lobster",
    "water",
    x => Alert("pot " + x)
);

или же в одну строку, тогда тоже будет понятно
Нет, этот код ужасен не по этой причине.
Этот код перегружен синтаксисом ЯП, также нарушена инкапсуляция, т.к. наружу торчит реализация:
x => Alert("pot " + x)
Ваша реализация Cook не учитывает что количество ингридиентов может быть не равно 2м (у Спольски тоже), но тут явно видно что их может быть разное количество.
Думаю, с помощью блоков в Objective-C можно решить описанные задачи.
вкратце можно резюмировать статью так: как хорошо ваш язык поддерживает ФП?
C это умеет уже черт знает сколько лет!
Может кто-то из профи прояснить один момент?
Меня всегда настораживало использование в одном контексте фраз, типа «повторное использование кода» и анонимных функций. Объясню. Возьмем (любой) пример из топика:

Map((x) => x * 2, list);


Здесь у нас достаточна простая анонимная функция (x) => x * 2. Предположим, что вместо нее что-то посложнее, но не в этом суть. Вопрос в следующем. Я не отрицаю удобство и наглядность такой записи, но если мы используем подобный кусок кода многократно в самых различных местах программы, разве это не противоречит тезису «повторного использования окда кода»? Ведь получается, что мы код этой анонимной функции будем повторять многократно по коду программы. В моем понимании «повторное использование» выглядит как написание кода один раз и затем уже его многократный вызов, где необходимо. Т.е., тут больше подходят именно именованные функции методы. Да, я понимаю, что такой подход не влияет на производительность, но разве он не дублирует многократно один и тот же код? Что, по-моему, избыточно. И тут вопрос к продвинутым специалистам, может компилятор настолько умный, что одинаковые анонимные функций сводит к одному коду и уже вызывает только его? Как, например, многократное использование одной и той же строки в разных участках кода.

Может я излишне придираюсь, просто моим первым ЯП был asm, это не могло не отпечататься на моем мозге и восприятии действительности. Шарп люблю, использую. Интересуюсь не троллинга ради, а исключительно ликбеза для.
Нет — на каждую анонимную функцию компилятор создаёт отдельный метод. Компилятор не настолько умный. Может это и к лучшему, а то как бы он тормозил бы тогда.
Ну, не скажите. Если взять любой проект (кроме Hello Word), то наверняка вы найдёте десятки кусочков кода вида
if (something > 0) { ... }

вы же не будете заменять все такие сравнения на вызов функции
IsPositive(something)


Чем же тогда принципиально отличается использование анонимной функции в следующем коде:
list.Where(item => item > 0)

В вашем примере, уже не нарушение ООП, а нарушение структурного программирования. Когда Вы пишите if (something > 0) {… } Вы внутри if выполняете любые произвольные действия и получаете результат. Т.е. вы декомпозируете алгоритм на части — есть вход something, и вы формируете выход, где то в {… }.

Что происходит в list.Where(item => item > 0), вы передаете в функцию Where не чистый, численный результат, а выражение, которое еще не вычислено. Т.е. декомпозиции на функции у вас нет, у вас все в каше внутри Where. Where оперирует не с полученными данными, а с тем что еще предстоит вычислить. Т.е. внутри where по сути будет вычисляться something > 0, но не явно. Если Вы под item закините в where, что-то другое, да еще поменяете выражение, where вычислит другое выражение.

Поэтому если в коде у меня 10 строк if (something > 0) {… } — то это дублирование. И я это вижу. Но десять выражение вида list.Where(item => item > 0) — еще не означает, что есть дублирование, все зависит от того, что такое item. Т.е. код становится зависимым от определения свыше, но при этом ничего не упрощает. Существует ли дублирование становится найти значительно сложнее. Тем более многие любят применять тут не строго типизирование, а пишут типа var adress — в итоге наслоение проблем на голом месте.
Первые два абзаца я просто тупо не понял. Простите. Может я тупой, может я просто не выспался, может вы ерунду написали…

Насчет третьего абзаца. Если вы не можете распознать, есть ли у вас дублирование, значит вы не видите связь кода в вашей системе. Если у вас в коде 10 строк if (something > 0) {… } — значит у вас уже проблемы и без использования LINQ/Enumerable-расширений.

Теперь, по поводу var. Во-первых, код с var остается статически типизированым — это обеспечивает компилятор. Во-вторых, иногда не применять var невозможно. См. страницу msdn.microsoft.com/ru-ru/library/bb383973.aspx:
Однако во втором выражении использование типа var обязательно, потому что результатом является коллекция анонимных типов и имя этого типа является доступным только для компилятора
Боюсь вы действительно ничего не поняли, и ответили не в попад.
Я сказал, что если вы не можете понять, где именно у вас дублирование — у вас проблемы уже и LINQ/Enumerable-расширения тут не причем + придрался (заметьте, полностью обоснованно) к вашей критике var.

Почитал сейчас в вики о структурном программировании. Вы должны понимать, что декомпозиция программ уже давно находится вне уровня ветвление/цикл, а программисты оперируют более высокими абстракциями. Есть как минимум функции (они входят в СП, но весьма ограниченно), а функции согласно ФП могут передаваться как аргументы, ими можно манипулировать как обычными данными (это не входит в СП). На самом деле частичка функционального программирования серьезно упрощает код (тем более, ФП — это тот еще «баян», 60-е года между прочим). Поэтому я действительно не могу понять ваших претензий к использованию LINQ/Enumerable-расширений.

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

Попробуйте написать ваши претензии в более расширенном виде.
>«если в коде у меня 10 строк if (something > 0) {… } — то это дублирование. И я это вижу.»
>> если вы не можете понять, где именно у вас дублирование

Вот как можно в мои слова вкладывать совершенно противоположный смысл?
Я имел в виду, если у вас 10 строк if (something > 0) {… } и вы это еще не исправили (а должны были как минимум на 4-м, а лучше сразу на 2-м if'е), то значит уже проблемы.
«Придирка» к критике var — вообще мимо, там пример как раз использования LINQ, соответственно использовать var вынуждает именно LINQ. Это плохо.
Да нет, не плохо, он обеспечивает минимум синтаксиса.
Функции всегда можно было передавать как аргументы, и для это не нужен был LINQ никогда.
> использование лямбд не отменяет типобезопасности

это лишь на уровне компилятора. Но прозрачности и четкости кода это не добавляет, а размывает. Приходится просматривать весь код, чтобы понять какого типа стал var. Т.е. вместо того, чтобы смотреть определение типа, приходится смотреть алгоритм его определения, что далеко не всегда тривиально.
Теперь главное — ну как можно говорить, когда вы не знаете терминов «структурная декомпозиция» и «объектная декомпозиция», и чем они отличаются и что обеспечивают? Отсюда вы с легкостью пишите спагетти код, и думаете, что это вам помогает упрощать код. Простота кода не измеряется числом операторов, она измеряется возможностью повторного использования. При анонимных методах — нет возможности что-то использовать повторно ни в объектном, ни в структурном смысле.
А что, ваша IDE не умеет показывать выводимый тип? Моя VS показывает, достаточно подвести курсор к имени переменной.
Реализация этого сделана, конечно хорошо — это только благодаря С#+VS, но это не отменяет проблемности. Типы надо задавать, а не выводить. К примеру, вы работаете с SQL сервером, и передаете туда в качестве параметра int, хотя он у вас определен как var. Но достаточно вам расширяя код, изменить правила вывода типов, и ваш var может оказаться string. Проверка типов выгружаемая в SQL проверена не будет.
Мой комментарий выше был к другому вашему комментарию.

По поводу возможности повторного использования. А вы не думали, что это уже не возможно? Или как минимум не имеет смысла? За вас придумали LINQ/Enumerable — одну из самых клевых абстракций, вынесли то, что писалось во всех проектах вручную через for/foreach в .NET, не забыв обеспечить относительную ленивость. А в анонимных методах — какое еще повторное использование? Большинство анонимных методов занимают от силы символов 20, не больше, разработчики выносят действительно большой код в именованные методы.
> Большинство анонимных методов занимают от силы символов 20, не больше, разработчики выносят действительно большой код в именованные методы.

Вы можете найти границу, где «действительно большой код» от того, который допустимо использовать в анонимных методах?

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

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

так вот аннонимные методы это скрывают
Как это они скрывают? Поясните мысль, пожалуйста
Ну, выше я пояснял — вы не поняли. Давайте зайдем с другой стороны. Видите ли в функциональном программировании, на самом деле повторное использование имеет несколько другой смысл. Это собственно выражение функции, по сути формулы. Как известно формулу можно применять к разным данным. Вот данное выражение как и в математики совершенно абстрактно item => item > 0. Т.е. в функциональном программировании функции отличаются не смыслом, а выражением. В то время как в объектном, temperature > 20 и brightness > 20 имеют разный семантический смысл, что и отражается именованными процедурами. В ФП будет не различимое item => item > 20. Что стоит за item не выделено, не декларировано, а соответственно в коде не видно/скрыто…
Простите, я правильно понял, ваша претензия относится к тому что мы пишем list.Where(item => item > 0), а не temperatures.Where(temperature => temperature > 0) или weatherInfoCollection.Where(weatherInfo => weatherInfo.temperature > 20 && weatherInfo.brightness > 20)? Т.е. я имею в виду, вы не знаете, что в лямбда-функциях мы можем как угодно именовать входные аргументы?
Дело не в именовании, а в типизировании.
А что типизирование? Оно остается статическим, используется вывод типов, среда может показать какой тип у аргументы, какой у результата функции. Ну если хотите, указывайте типы, но имхо, это идиотизм. Вот пример, как указывать типы можно:

var firstExample  = list.Where((int x) => x > 0); // указываем тип аргумента
var secondExample = list.Where(new Func<int, bool>((int x) => x > 0)); // указываем тип результата вычисления лямбды и еще дважды аргумента
var thirdExample  = list.Where(new Func<int, bool>(x => x > 0)); // тип результата вычисления лямбды и аргументов.
var fourthExample = list.Where((int x) => (bool)(x > 0)); // или так // приведение к типу мешает компилятору скомпилировать неправильный код
List<int> fifthExample = new List<int>().Where((int x) => (bool)(x > 0)).ToList(); // да указывайте что угодно, только зачем?
Впрочем когда вы пишите в нотации weatherInfoCollection.Where(weatherInfo => weatherInfo.temperature > 20 && weatherInfo.brightness > 20) уже лучше. Лучше видно, в чем состоит ошибочность. Условие weatherInfo.temperature > 20 && weatherInfo.brightness > 20 — это особое характеристика weatherInfo, она имеет некую семантическую значимость, назовем ее «Хорошая погода». Необходимость того, что Вы пишите через точку weatherInfo.«Какое-то свойство» указывает на «завистливость» к объекту weatherInfo. Соответственно, правильным будет условие weatherInfo.temperature > 20 && weatherInfo.brightness > 20 инкапсулировать внутри объекта weatherInfo.

Таким образом нужно вводить Сalendar.GetGoodDay(). Вот тогда это будет объектно. Как будет внутри — это уже второе дело, естественнее конечно внутри пройтись циклом, чем считать .Where аналогом цикла. Но главное, что здесь мы семантически засунули анонимную функцию в вполне себе объектный метод. Но дело то в том, что так делают редко.
Таким образом нужно вводить Сalendar.GetGoodDay(). Вот тогда это будет объектно.

Синдром архитектора detected. А если не мы писали класс Calendar? Не, выносить в отдельную функцию со звучным название оно конечно полезно, но не стоит забывать, что есть риск потеряться в коде, прыгая между мелкими функциями размазанными по всему классу. Функция — это как минимум пять строк — заголовок, тело, закрывающая фигурная скобка (}) + добавьте две строки отступов от других функций + три строки очевидных xml-комментарий (если у вас суровые требования к документированию кода), ибо тело функции состоит из одной строки — итого в среднем 8 строк. Лямбда — это одна строка.

Потом, вы кажется не совсем поняли, что такое .Where. Эта функция действительно не аналог цикла. Это «ленивый цикл». .Where не вычисляет все. Вычисление с помощью .Where вы можете описать так: foreach вы обрываете с помощью break, а потом продолжаете с того места, где оборвали вычисление. Т.е. вы можете в одном слое отдать ленивый список хороших дней, а в другом применить еще какое-то ограничение и вычислить только первых 5 хороших дней, удовлетворяющих условию. Если вы пишите библиотеку, то вы не можете знать всех вариантов использования, а программист, использующий вашу библиотеку не будет знать, сколько ему нужно забрать данных (поскольку 5 дней должны удовлетворять еще какому-то условию, т.е. всего ему может быть нужно забрать 10 дней, или 5, или 100), т.е. написав функцию TakeNDays(int n), вы только усложните пользователю вашей библиотеки жизнь.

P.S: Я надеюсь вы хоть foreach используете, а не for?
> А если не мы писали класс Calendar?

Делаем оболочку, путем наследования, или агрегирования. На крайний случай расширяем

> Лямбда — это одна строка.

Это обманчивое впечатление. Символы мы не экономим.

> Если вы пишите библиотеку

До этого мы говорили не о библиотеке! Если бы это была библиотека — то нужно использовать нормальный SQL в виде строки, а не этот ужас псевдо-SQL`a. В любом случае, требовать от пользователя библиотеки использовать LINQ — это маразм.
Не экономим, говорите? Сравните:
weatherInfoCollection.Where(weatherInfo => weatherInfo.temperature > 20 && 
                                           weatherInfo.brightness  > 20);

И отдельная функция
static class WeatherInfoExtension { // 
	// <...> одна пустая строка отделяет эту функцию от остальных функций сверху
	/// <summary>
	/// Returns true if day temperature is greater than 20 C and day brightness greater than 20 L and false in other cases.
	/// </summary>
	public static bool IsGoodDay(this WeatherInfo weatherInfo) {
		return weatherInfo.temperature > 20 &&
		       weatherInfo.brightness  > 20;
	}
	// <...> одна пустая строка отделяет эту функцию от остальных функций снизу
}

Итого 9 строк против двух (или 8 (ладно, 5, но за отсутствие комментария к функции дают по голове)) против одной, если условие не разбивать на две строки) + еще две на объявление статического класса. Кол-во символов посчитать сами сможете? Ой, я забыл, мы же только объявили функцию, нужно же её еще использовать (я надеюсь, вы не против foreach?). Еще 3-4 строки:
List<WeatherInfo> goodDays = new List<WeatherInfo>();
foreach(WeatherInfo weatherInfo in weatherInfoCollection)
	if (weatherInfo.IsGoodDay())
		goodDays.Add(weatherInfo)


Все еще говорите, что LINQ и анонимные методы отстой?

И потом, я говорил о навигации по коду, а тут не кол-во символов, а именно количество строк решает удобство навигации.

До этого мы говорили не о библиотеке!
Ну и что, что не говорили? Язык/.NET не только для вас родного пишется, а для всех программистов, а у них потребности разнообразные.

Если бы это была библиотека — то нужно использовать нормальный SQL в виде строки, а не этот ужас псевдо-SQL`a. В любом случае, требовать от пользователя библиотеки использовать LINQ — это маразм.
Ага, т.е. заставлять юзера использовать SQL там где он не нужен (а я вам описывал случаи, когда синтаксис LINQ удобен, но совать туда SQL нельзя — список задач (taskmgr.exe), список файлов/каталогов (файловый менеджер), содержимое архива (архиватор)). А если у нас библиотека делает что-то другое, например, лениво вычисляет простые числа? Тоже туда совать лишний слой в виде SQL?

Признайте что LINQ/Enumerable-расширения удобны для некоторых задач. Того, что вы начнете его адекватно оценивать, я уже не ожидаю.
LINQ и анонимные методы отстой

Вот именно не экономим, т.к. нет смысла экономить на понятности кода и хорошем его оформлении. Отдельная функция тут на порядок лучше, чем строка в которую понапихано все сразу.

> заставлять юзера использовать SQL там где он не нужен

нет пока мы говорили только о примерах, где был нужен SQL

> список задач (taskmgr.exe), список файлов/каталогов (файловый менеджер), содержимое архива (архиватор))

что-то я не помню примеров где вы показавали, как хорошо тут использовать LINQ и анонимные методы.

> Признайте что LINQ/Enumerable-расширения удобны для некоторых задач

Я сам их иногда использую, но все это просто от лени писать нормально, и это самое «вонючие» место в коде, с которого уважающий себя программист начинает рефакторинг.
1. Нет, отдельная функция не всегда лучше.
2. Да нет, мы не говорили о исключительно SQL. Кажется вы слишком помешаны на SQL, чтобы адекватно обсуждать LINQ.
3. Поищите в треде, я уже указывал эти примеры.
4. Что-что? Нет, вы конечно можете писать хреновый код с помощью LINQ, но это исключительно ваша вина. LINQ/Enumerable дает возможность писать хороший функциональный код.
Если кто на чем и повешен, то это вы на LINQ :), причем так что не способны воспринимать информацию.
На ФП скорее, только мне нужно еще многое из ФП поучить
Ну, тогда хотя бы меня догоняйте — тогда и поговорим :)
Вы считаете, что умнее меня — окей. Но мне интересно, как вы соотносите свои мозги с теми мозгами, которые разработали .NET и три официальных языка? (Basic, C#, F#)
Я считаю, что достаточно знаю о ФП, чтобы мне не советовали его учить :), особенно те кто не очень то в ООП. Что же касается .NET у меня к ним претензий нет, они зарабатывают деньги и удовлетворяют текущий попсовый спрос. Вопрос к тем, кто гонится за попсой…
Не волнуйтесь, я в ООП также как в ФП и ИП. Судя по всем остальным топикам, ваше ООП считают фиговым, так что не ставьте себя выше всех. Ваш опыт в программировании не дает вам права считать себя умным, вполне возможно, что вас таким просто считают некоторые, а от остальных вы отмахиваетесь. Не обижайтесь, если что
Я никогда не отмахиваюсь от серьезных аргументов, и часто признаю если таковые есть… пока, увы…
Окей. Вот вам серьезные аргументы — 1) унифицированность 2) ленивость 3) лаконичность кода. Вы можете с легкостью послать 3-й аргумент («о вкусах не спорят»), то первые два вы не можете так уж просто обвинить в несерьезности
> унифицированность

Поясните.
Один стандартный код, который используют все сторонние библиотеки — это прекрасно. Чем хороша ленивость я уже объяснил. Если каждая левая библиотека будет создавать свой вариант ленивости — это будет плохо.

Если у вас возникнет вопрос: почему ленивый LINQ нельзя заменить на foreach/if/break, то, тут нужно много расписывать, почему это будет плохим решением, но самое главная причина — есть хорошее стандартное решение.
Ну, что тут можно сказать — «боже, избавь меня от таких стандартов» :)
> почему ленивый LINQ нельзя заменить на foreach/if/break

Можно и нужно :)

> нужно много расписывать, почему это будет плохим решением

если вы о т.н. «чистоте», проходите мимо — сказки детям перед сном.
Не, не о чистоте, а о том, что пользователю библиотеки иногда будет необходимо получить не более N элементов коллекции, сгенерированной библиотекой, причем N будет зависеть от содержимого. Т.е. вот тот банальный пример — нужно получить 5/10/15 хороших дней без лишних вычислений, а разработчик библиотеки не знает что такое хороший день (предполагается, что условие «хорошего дня» может быть закрученным). Т.е. на самом деле 5 хороших дней подряд может не встречаться, а быть рассредоточенным среди 100 дней. Так вот, дабы не делать лишних обращений/выделений к памяти, нужна ленивость. Либо вы ее будете обеспечивать руками, либо вам её обеспечит LINQ. Но вообще пример фиговый, просто возьмите такой же пример, но не с данными, а к примеру, со сложной функцией.
Это решается по другому. В том месте, где разработчику надо знать хороший это день или нет, он вызывает т.н. обратный вызов (callback), делегат на С#. А чтобы гарантировать, что пользователь обеспечит такую функцию, надо её ссылку потребовать как аргумент конструктора класса, которым он захочет пользоваться. Вот и вся не хитрая математика.
Зачем так делать разработчику библиотеки, если есть прекрасные стандартные средства, обеспечивающее то же самое?
Потому, что это и есть стандартный классический прием, без тяжеловесной LINQ-библиотеки.
Ок, мне лениво дальше пытаться убедить вас, кажется вы вообще не слушаете.

Хочу подвести итог того, о чем я говорил:
— LINQ/Enumerable-расширения — это часть стандартной библиотеки и глупо этим не пользоваться
— LINQ обеспечивает практически все, что можно обеспечить (единственное, linq не может обеспечить для таких функций, как n-е число Фибоначчи — возможность получить n-е число за logN — это нужно кодить отдельно, как впрочем и для всех рекурсивных функций)
— LINQ предоставляет единый интерфейс управления последовательностями/коллекциями. Что для списка задач в taskmgr.exe, что для БД.
— LINQ позволяет работать с последовательностями как абстракцией и таким образом помогает фокусироваться на главном
— LINQ обеспечивает ленивость вычислений
— ФП — это полезно для кода и ума.
— Вам нужно разобраться, что такое LINQ
Похоже, не слушаете Вы. Но мне лениво делать резюме, слишком много.
Ну, и наверно излишне повторять, что каждый ваш аргумент мы обсудили и я показал, что он как минимум спорен.
Вы кстати, в курсе т.н. «прямом и обратном логическом выводе»? Так вот это и есть сердце «вашего» ФП… это отбракованная для ИИ-методов старейшее направлении… это как в космосе, не пошло давай выкинем гражданским, они докрутили туда ООП (а Haskell полностью поддерживает объектность в смысле операций логического вывода), с другой стороны снабдили старыми сетевыми базами (теперь это имеет громкое название NoSQL) — и вот вам выкидыш — пользуйтесь.
Там проблема лишь одна — реально программировать в терминах ограничений практически не возможно. Их просто не откуда взять в реальной задаче, и нужно эммулировать. Т.е. как ни крути будешь указывать, что делать по шагово, а идею о том, что ты напишешь ограничения и компьютер сам тебе на тарелочки все разложит — это мечта идиота. Но привлекательность ФП считаю только от не понимания этого факта.
Вы не верите в математику? Зря, зря. Впрочем, я считаю, что от императивного программирования (из-за отсутствия побочных эффектов ФП недостаточно быстро, много памяти) мы в ближайшее время никуда не денемся, но количество ФП увеличится довольно сильно. Причин тому несколько — ФП можно анализировать относительно простыми математическими методами. Помните байку о том, что скомпилировать программу на хаскеле практически невозможно, но если вы таки её запустите, то скорее всего она будет рабочей? Уверены, что это всего-лишь байка?
Мат. методы будут развиваться. Частота процессоров скорее всего не будет сильно расти (это просто наблюдение, которое было описано в одной статье, если хотите — могу поискать, дать ссылку), но будет увеличиваться количество ядер, кол-во и скорость памяти, а объемы обрабатываемых данных расти — это будет способствовать ФП. Тот же классический MapReduce — это чистое ФП по своей сути.
Просто возможности конкретно этой математики, я если и не знаю, то хорошо представляю. Но а параллельное программирование никто не отменял :)
Математика не стоит на месте, развивается. Мат. методов для ИП вообще практически нет.
> Мат. методов для ИП вообще практически нет
и не будет, это проблема математики, а не ИП. Не может большие быть описано меньшим.
Ой ли… Если ИП плохо анализировать, значит математика плохая?
Значит математика не предназначена для этого, и не может быть предназначена.
И главное — это может лишь казаться, что построение алгоритмов, это лишь часть математики. На самом деле построение алгоритмов имеет куда большую ценность, чем чистая математическая логика. Математика мало выразительна и не имеет семантики. В то время как ООП заполняет этот недостаток, заполняет смыслом, и становится возможным говорить о кибернетических экспериментах, а не математических расчетах. А это две большие разницы.
> ленивость

А что она дает? Скорость вычислений? Почему же тогда на каждом заборе написано, что ФП работает на порядок медленнее? Что это «практика расходится с теорией»?

Смотрите: с точки зрения алгоритмической сложности, что ФП, что ИП — одинаковы, т.е. у них одинаков порядок роста. Т.е. один и тот же алгоритм записанный и в ФП, и в ИП будет иметь порядок роста допустим, O(N), но помимо порядка роста на время работы метода влияет еще и константа. Т.е. время_работы_метода = константа * N.
Но процессору ближе ИП (процессору, кстати, ближе ассемблер/C, а не всякие C++ и ООП), поэтому у ФП константа больше, и алгоритм будет работать медленнее (на самом деле, поскольку чистое ФП не имеет побочных эффектов — там еще появляется необходимость в неизменяемых структурах и соответственно в копировании туда-сюда данных, но это имеет свои преимущества).
Поэтому ФП преобразованное в ИП на данный момент выполняется медленнее, но это временно — сейчас идет мода на многоядерные процессоры (оказалось, что мы подошли к пределу, когда частота процессоров растет очень медленно). А через некоторое время с ним случится то же, что случилось с ассемблером — процессоры усложнятся настолько, что в большинстве случаев код написанный человеком на C будет оптимальнее кода написанного человеком на Asm, а казалось бы — если C превращается в Asm, то код на Asm должен быть быстрее.

Но я отошел от темы. Ленивость позволяет увеличив скорость работы в худшем случае, увеличить скорость работы в лучшем случае. Т.е. допустим, у нас есть коллекция которая содержит 100 тыс. элементов. Пускай нам приходится много манипулировать содержимым коллекцией, но большинство (в соотношении 10 к 1) запросов/манипуляций будут требовать только сотни элементов. Было бы глупо применять одну и ту же функцию ко всем 100 тыс. элементов. Если Where работает в 10 раз медленнее чем foreach (как на самом деле — не знаю, мерять надо), то выгода очевидна.
Но если нам нужно обработать всю коллекцию, то с точки зрения скорости естественно лучше использовать foreach, если программа проседает в этом месте.

Вот зачем нужна ленивость.
Ну, желание лучше жизни ФП-шники конечно хотят, очевидно если что-то захордить в железо — будет работать лучше. Вот методы искусственного интеллекта — тоже ждут своего железа. Но имеем что имеем.

Ленивость выглядит хорошо. Но SQL-запросы также оптимизируются, ничем ни хуже как минимум. Компиляторы на то и пишут, чтобы оптимизировать вычисления. И мне вообще-то как программисту об этом знать теоретически не нужно, конечно, практически надо оптимизировать в ручную, но как правило, только самые узкие места. И не поверишь if тоже лениво вычисляет аргументы :) а break чего стоит ;)
Повторяю в очередной раз: LINQ используется не только там, где можно припихнуть SQL. Но даже там где можно, он может быть довольно удобным.

По поводу if-а формально это неправда. У If аргумент один (условие) + он выполняет участок кода, если это условие истинно — это не ленивость. Если ты имеешь в виду, что он не вычисляет вторую функцию в if (a() && b()) — это всего-лишь следствие «ленивости» операторов &&, ||. Break — ну действительно долго формулировать почему это не то же самое, что и ленивость в LINQ.
> не ставьте себя выше всех.

Я этого никогда не делал, в отличии от вас, который решил меня отправить учится, и ряда мои оппонентов, которые заблуждаются во многих вещах, но готовы зато заявить о неадекватности оппонента.
Слушайте, ну я же не просто так с горы взял и заявил. Я реально пытался понять ваши аргументы. Максимум что у вас получилось — чуть-чуть сместить мои приоритеты от анонимных методов в сторону именованных методов, ну да это ничего, я еще Фаулера почитаю/проработаю и буду больше знать/понимать.

Проблема в том, что если бы я один такой был бы, который ваши аргументов не воспринимает — я бы задумался о том, что это со мной что-то не так. Но не я один вам «фе» говорю
Ну, вот видите — вы пытаетесь думать. Хорошо. Кое кто не хочет думать. Плохо. И также плохо ориентироваться на тех которые говорят не думая. Как там мне сказали как то «миллион мух не может ошибаться?» :) И это не проблема — это недоразумение.
Дело даже не в анонимных методах, а в ленивости LINQ.

P.S: Вы всерьез считаете, что код который по объему в раз 5-10 больше, является более понятным? Ведь лапши то нет.
Понятливость кода не зависит от объема.
> код который по объему в раз 5-10 больше, является более понятным

В данном случае да, т.к. LINQ ничего не сокращает по сути, все то что не дописано или безалаберность при написании или нужно все равно домысливать при прочтении.
Да где оно там не дописано. Написано все, задокументировано. Если вам нужно указывать типы — можете даже указывать типы. Но фокус в том, что LINQ — стандартное средство языка, обеспечивающее ленивость вычислений (в том числе нет необходимости копировать туды-сюды содержимое). Я не понимаю, почему вы против стандартных средств.
Это такое же стандартное средство как MFC в Си :) — это лишь одна из многих библиотек.
Вы бы лучше сравнили с STL в C++, LINQ — все-таки часть стандартной библиотеки.
> Написано все, задокументировано.

Меня удивляет ваша избирательность к if вы добавил комментарий, ак своей строке LINQ — не посчитали нужным — это такой стиль сейчас у ФП-шников :) — хреново тогда…
Я же показал вам, что в любом случае у отдельной функции выходит как минимум 5 строк (1 на заголовок, 1 на тело, 1 на закрывающую скобку + 2 обрамляющих пустых), а у анонимного метода — 1 строка. Я просто показывал вам все возможные расчеты кол-ва строк. Почему я добавил комментарий? — потому что в некоторых компаниях вполне могут быть суровые правила насчет документации к методам. Т.е. комментарии внутри метода никого особо не волнуют, а комментарии к методу превращаются в документацию.
Вы сами замечаете как вы формально подходите к данному вопросу? И поверьте мне, что с таким отношение в любой компании будет «сурово». Функции выделяют ни для того, чтобы экономить строки. И не зачем вообще экономить строки. Поймите, что если бы была цель сэкономить — вместо where, можно было бы создать метод forif с более четкой нотацией. Но дело то не в этом, дело в том, что вы выделяя функцию выделяете смысловой аспект. Если экономить на этом весь код будет в кашу. LINQ прячет эти смысловые аспекты (об этом я уже говорил). Если же вы будите осмысленно писать код, то разница будет только в синтаксисе и не в чем больше.
ADT — алгебраический тип данных

Вообще, дело даже не в анонимных методах. Ерунда это все, по поводу количества строк — это просто вопрос удобства навигации по коду. Личный вопрос.

Дело в том, что LINQ/ФП позволяет держать все на одном уровне абстракции в функции. Я в одной умненькой книге прочитал такое волшебное правило, что код в методе нужно держать на одном уровне абстракцией — чтобы не было резких перескоков. Да, это правило, кстати, немного противоречит моей любви к анонимным методам, но зато полностью поддерживает наличие LINQ. (Абстракция — это то, что прячет свои детали, но зато позволяет думать о коде на один уровень выше — нелепое определение, но я надеюсь вы поняли что я хотел сказать)

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

Итак, какова ваша причина не использовать LINQ вообще?
> какова ваша причина не использовать LINQ вообще?

Ну, почему вообще? Я уже признавался, что иногда грешу этим.

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

Но LINQ как я говорил выше косвенно ведет к «бороде», к «вонючим» местам в коде… не потому, что там используется именно LINQ, а потому, что наверняка программист скорее всего поленился сделать это правильно. В итоге важная семантическая вещь осталась скрыта внутри кода. Нет конечно, я теоретически допускаю, что есть такие профессиональные программисты, которые несмотря на использование LINQ могут писать качественный код, но статистически уверен, что это будет не так. Это все равно, что языки с нестрогой типизацией стимилируют писать плохой код, хотя можно писать и хороший.
Ваше заявление о LINQ для меня видится примерно как все что можно сделать с for, можно сделать с while, все что можно сделать с while, можно сделать с goto.

Мне все-таки до сих пор непонятно, почему вы решили, что LINQ стимулирует написание плохого кода.
> 3. Поищите в треде, я уже указывал эти примеры.

Нет вы ничего не указывали, если только ник сменили?
И где там код?
tac, а расскажите о своём опыте программирования. когда начали вообще, когда начали программировать профессионально, на чём пишете, в какой области работаете?

просто вы говорите крайне необычные вещи, и если бы вы, скажем, говорили не о программировании, а темы за жизнь задвигали, то чтобы звучать убедительно, вам потребовалось бы провести демонстрацию с пятью хлебами и накормлением хабра и превращением воды в вино. ну потому что вы несёте полную ахинею в таком количестве, на которое я был способен только один раз в своей жизни, после того как отравился в индийской дхармсале какой-то едой. но я-то делал это не головой и руками, как вы!
Я не обещал код, я обещал примеры в которых SQL нахер не нужен, а LINQ выглядит прекрасно
0. Учить ФП, учить ФП и еще раз учить ФП. Хотя бы основы
Я только что писал о подобном, но, имхо, вы совершенно зря приплели сюда ФП и ООП. Это вопрос именования, к парадигмам он отношения не имеет. В ФП допустимы функции с одной формулой, но разными именами, а в ООП встречаются функции/методы с ясной алгоритмической начинкой, но совершенно не понятным смыслом. И поднимая вопрос об повторном использовании кода (хоть анонимного, хоть именованного) никто, вроде, не предлагает выделять его чисто механически хоть в ФП, хоть в ООП.
nsinreal, пользователь tac, с которым вы пытаетесь беседовать, невменяем. почитайте хотя бы пост habrahabr.ru/post/144645/

ну то есть полностью невменяем. как будто он живёт и работает на кокаиновой фабрике. у вас 0 шансов объяснить свою точку зрения.
Нет, ну действительно интересно, может tac действительно что-то эдакое знает. Хотя, такое впечатление, что нет.
Кстати, частенько заменяю, но не на IsPositive, а, например, на HasError если в конкретной проверке использую знание того, что положительное значение является признаком ошибки. Два плюса: главный — проще понять смысл проверки увидев if (HasError(something)) в коде, чем if (something>0); второй — если решу, что признаком ошибки должно быть отрицательное значение или только ноль, то изменении в проверки нужно будет ввести только в одном месте. Минусы — при оптимизации бывает нужно обратно «инлайнить», но тогда не забудешь поставить комментарий, более того это можно сделать автоматически, скажем при помощи sed :) Ещё вариант IsValid() вместо > 0. Аналогичные плюсы. В общем практически везде, кроме тех мест где тупо используется математика, а не кодирование какой-либо информации числом. Если кодирование, то пытаюсь вынести в отдельную функцию/метод саму проверку, а в условии оставить её семантику в виде имени этой функции. Да и в чисто математике, хотя она и редко встречается :(, пытаюсь порой придать семантику проверкам, скажем не if (D>0) {//...}, а if (HasRealRoots(D)) {//...}
Я вообще не профи, но по-моему, до того как вынести два одинаковых участка кода в метод, нужно выяснить почему они одинаковые. Если так совпало (да, и такое бывает), то нужно оставить все как есть. Иначе когда мы поменяем что-то в методе, то это затронет оба участка кода — даже тот, который мы не хотели менять. Иногда это входит в наши намерения — потому что эти участки кода функционально одинаковы (или как это называется), а иногда это не входит в наши намерения — потому что участки кода просто совпали. Собственно именно поэтому мы и выносим одинаковые участки кода в метод — чтобы не бегать по коду, когда нужно что-то взять и поменять. Ну и конечно же, дабы повысить уровень абстракции.
Для указанной лямбды, её в принципе можно вынести в Double(x) и в принципе, так будет лучше с точки зрения «код должен читаться как английский язык», но не всегда такое можно проделать. К примеру, если у нас будет лямбда (x) => 2 * x — 1, то вы её не вынесете и не придумаете адекватного имени. NthOddNumber(x) не всегда будет выражать ваше намерение относительно x.
Кроме того, в анонимных функциях, я думаю, много кода (по кол-ву символов) никто не пишет, ибо тогда он превращается в лапшу. + Скорее всего анонимные функции будут размазаны по проекту и вам вряд-ли удастся определить что есть одинаковые функционально участки кода.
Для лямбды (x) => 2 * x, имхо, нужно придумать имя отличное от Double в большинстве случаев. Тогда не нужно его будет менять на другое при изменении её на (x) => 2 * x — 1 :) Смотреть не на содержимое функции, а на контекст её использования. Если это система построения графиков, то назвать её просто Function, если это формула начисления бонусов в игре, то назвать её Bonus и т. п.
А насчёт «в анонимных функциях, я думаю, много кода (по кол-ву символов) никто не пишет, ибо тогда он превращается в лапшу.» посмотрите в какой-нибудь JS проект с активным использованием колбэков при, например, аякс-запросах. Иной раз вообще нет ни одной именнованной функции на много килобайт кода…
Имхо, C# таже как и Java предназначены совсем для другого класса решений нежели JS или куча других скриптоков языков, которые могут это. И другая сторона такой легкости — ужасный спагетти из nested фунькций, которую очен сложно поддерживать.

Так что это не плюс для С# а минус :)
Тот факт, что Google изобрел MapReduce, а не Microsoft, говорит немного о том, почему Microsoft все еще играется, пытаясь заставить заработать элементарный поиск, в то время как Google уже решает следующую проблему — как построить Skynet^H^H^H^H^H^H — самый огромный компьютер для параллельных вычислений.
Всегда думал что над алгоритмами в гугле и в микрософте работают не программисты, а математики.
Шоколадный лось — это, наверно, не очень вкусно.
Sign up to leave a comment.

Articles