Приводит ли использование выражений LINQ и Lambda к менее читаемому коду? [закрыто]

43

У меня беседа с коллегой по Linq, я напишу здесь:

Сотрудник: Давайте будем честными здесь. Синтаксис Linq отстой. Это сбивает с толку и не интуитивно понятно.

Я: да ладно, более запутанно, чем T-SQL?

Сотрудник: да.

Я: он имеет те же основные части, выберите, где и из

Сотрудник: Linq, для меня, является ублюдком реляционных + OO. Коллега: не поймите меня неправильно - это невероятно мощный инструмент, но они перенастроили SQL на использование коллекций объектов.

Я считаю, что использование Linq + Lamda очень мощно (он согласен), а также облегчает чтение кода (он не согласен с этим):

pickFiles = from f in pickFolder.GetFiles("*.txt")
where ValidAuditFileName.IsMatch(f.Name)
select f;

или

var existing = from s in ActiveRecordLinq.AsQueryable<ScannedEntity>()
where s.FileName == f.FullName && s.DocumentType != "Unknown"
select s;

или (код VB здесь)

   Dim notVerified = From image In images.AsParallel
     Group Join verifyFile In verifyFolder.GetFiles("*.vfy").AsParallel.Where(
      Function(v) v.Length > 0
      ).AsParallel
   On image.Name.Replace(image.Extension, ".vfy") Equals verifyFile.Name
     Into verifyList = Group
    From verify In verifyList.DefaultIfEmpty
    Where verify Is Nothing
    Select verify

Для меня это чисто и легко (по крайней мере, легче, чем альтернативы) читать, что вы думаете об этом?

Черный лед
источник
11
Люди вообще ненавидят перемены. Большой процент ненавидит это так сильно, что на самом деле боится.
Тони
9
Посмотрим правде в глаза ... linq - это просто тупой синтаксис функционального программирования, добавленный в C # и VB.Net. Поражение linq и лямбда-выражений в основном говорит "FP отстой". Это то, что говорит твой сотрудник. Я думаю, что дебаты были разгромлены в другом месте.
Скотт Уитлок
48
Беспокоит ли кого-нибудь еще, что люди склонны использовать слово «интуитивный», когда они действительно означают «знакомый»?
Ларри Коулман
7
Так или иначе, команда C # (включая Эрика Липперта) изо всех сил пыталась объяснить, что Linq не был портированием SQL, он был разработан с нуля, как и большинство других функций. Я бы сказал, что твой сотрудник - луддит.
Аарона
16
Для чего это стоит: я только что заставил свою жену (администратор офиса - практически нулевой практический опыт программирования) взглянуть на 3 примера aaronaught и смог понять смысл примеров linq и lambda гораздо проще, чем традиционный императивный пример.
Стивен Эверс

Ответы:

73

Я больше не могу найти нужный пост, но Эрик Липперт (и, возможно, несколько других софтинов) несколько раз высказывался о том, как Linq декларативен , что для нескольких классов проблем гораздо более интуитивно, чем императивный синтаксис.

Linq позволяет вам писать код, который выражает намерение , а не механизм .

Вы говорите мне, что легче читать. Это:

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    List<Customer> results = new List<Customer>();
    foreach (Customer c in source)
    {
        if (c.FirstName == "Aaron")
        {
            results.Add(c);
        }
    }
    results.Sort(new LastNameComparer());
    return results;
}

class LastNameComparer : IComparer<Customer>
{
    public int Compare(Customer a, Customer b)
    {
        return x.LastName.CompareTo(b.LastName);
    }
}

Или это?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return from c in source
           where c.FirstName == "Aaron"
           orderby c.LastName
           select c;
}

Или даже это?

IEnumerable<Customer> GetVipCustomers(IEnumerable<Customer> source)
{
    return source.Where(c => c.FirstName == "Aaron").OrderBy(c => c.LastName);
}

Первый пример - это просто набор бессмысленных шаблонов для получения простейших результатов. Любой, кто думает, что он более читабелен, чем версии Linq, должен проверить свою голову. Не только это, но первый тратит впустую память. Вы даже не можете написать это, используя yield returnсортировку.

Ваш коллега может сказать, что он хочет; лично я думаю, что Linq улучшил читабельность моего кода неизмеримо.

В Linq тоже нет ничего «реляционного». Он может иметь некоторые поверхностные сходства с SQL, но он никоим образом не пытается реализовать реляционное исчисление. Это просто набор расширений, которые облегчают запрос и проектирование последовательностей. «Запрос» не означает «реляционный», и на самом деле существует несколько нереляционных баз данных, которые используют SQL-подобный синтаксис. Linq является чисто объектно-ориентированным, просто он работает с реляционными базами данных через такие структуры, как Linq to SQL, из-за некоторого дерева выражений вуду и умного дизайна команды C #, делающего анонимные функции неявно конвертируемыми в деревья выражений.

Aaronaught
источник
6
+1 Если вам не нравится LINQ, вероятно, потому что «вы делаете это неправильно» :)
Darknight
1
Linq is purely object-oriented+1 за это. По этой же причине в нашем руководстве по стилю команды используется свободный синтаксис над синтаксисом запроса. Я обнаружил, что это делает ОО-природу ссылки более очевидной для тех, кто привык к объектной нотации в C-подобных языках.
ВОО
1
Я знаю, что посту 7 лет. Но вот вопрос: использовали ли вы функциональные языки в качестве основных инструментов до знакомства с Linq?
Sergey.quixoticaxis.Ivanov
4
Честно говоря, я предпочитаю цикл foor, потому что тогда я сразу вижу, где мы можем и будем иметь исключения NULL-ссылок, как обрабатывается ноль, и это не отложенное выполнение, которое затрудняет отладку, и это не создает n списков. также цикл for более эффективен.
затруднительное
1
@Aaronaught, если вы удалите сортировку и переформатируете процедурный код в стиле Horstmann , я скажу, что он более читабелен, чем версия LINQ . И в соответствии с принципом единоличной ответственности, сортировка на самом деле не принадлежит GetVipCustomers(), которая, как предполагает само ее название, должна возвращать только коллекцию VIP-клиентов в произвольном порядке. В тех редких случаях, когда важен порядок, например, вывод на экран, пусть вызывающая сторона сортирует коллекцию.
Ant_222
69

Сотрудник: Давайте будем честными здесь. Синтаксис Linq отстой. Это сбивает с толку и не интуитивно понятно.

Вы не можете спорить с этой критикой. Для вашего коллеги это отстой . Нам не удалось разработать синтаксис, который был бы для них понятным и интуитивно понятным. Это наша ошибка, и вы можете передать мои извинения своему коллеге. Я рад принять предложения о том, как сделать это лучше; что конкретно ваш коллега находит сбивающим с толку или не интуитивным?

Тем не менее, вы не можете угодить всем. Мое личное мнение и мнение большинства людей, с которыми я разговаривал на эту тему, заключается в том, что синтаксис понимания запросов гораздо более понятен, чем эквивалентный императивный синтаксис. Очевидно, что не все согласны с этим, но, к счастью, нам не требуется консенсуса всех миллионов наших клиентов при разработке языков.

Что касается того, что является «интуитивным», то мне вспоминается история английского лингвиста, который изучал много разных языков и, наконец, пришел к выводу, что английский язык был лучшим из всех языков, потому что в английском слова встречаются в том же порядке, что и вы. думай им . В отличие от французского, где они постоянно говорят что-то вроде «собака белая ест мясо красное». Как трудно французам думать слова в правильном порядке, а затем произносить их во французском порядке! Французский такой не интуитивный! Удивительно, что французы говорят на нем. А немецкий? где они думают, что «собака ест мясо», но затем должны сказать «собака ест мясо»!?!

Часто то, что является «интуитивным», является просто вопросом знакомства. Мне потребовались месяцы работы с LINQ, прежде чем я перестал начинать свои запросы с предложением «select». Теперь это вторая натура, и порядок SQL кажется странным.

Который это! Правила области видимости все перепутаны в SQL. Что-то, на что вы могли бы обратить внимание коллеги, - это то, что LINQ был тщательно спроектирован так, чтобы (1) введение переменных и областей происходило слева направо (*) и (2) порядок появления запроса на странице был порядок, в котором он выполняется. То есть когда ты говоришь

from c in customers where c.City == "London" select c.Name

c появляется в области видимости слева и остается в области видимости справа. И порядок, в котором происходят вещи: сначала оцениваются «клиенты». Затем «где» оценивается для фильтрации последовательности. Затем отфильтрованная последовательность проецируется с помощью «выбора».

У SQL нет этого свойства. Если вы говорите

SELECT Name FROM Customers WHERE City = 'London'

затем «Имя» вводится в область видимости чем-то справа, а не слева, и запрос выполняется в полном беспорядочном порядке; сначала оценивается среднее предложение, затем последнее предложение, а затем первое предложение. Это сейчас кажется мне сумасшедшим и незаметным, ведь я так долго работал исключительно с LINQ.


(*) Правила области видимости немного странны в LINQ с предложениями объединения. Но кроме этого, области видны хорошо.

Эрик Липперт
источник
5
Nitpick: На немецком языке «Der Hund frisst das Fleisch» на самом деле тот же порядок, что и на английском языке: «Собака ест мясо» :) Кроме того, я нашел синтаксис LINQ Query очень читабельным, как только понял, что это не SQL .
Майкл Стум
18
@gbjbaanb: Я не оправдываю синтаксис LINQ исключительно субъективным основанием его более понятного понимания . Скорее, я оправдываю это на совершенно объективной основе тем, что гораздо проще разрабатывать инструменты, которые помогают пользователю в построении запросов LINQ, поскольку синтаксис LINQ был разработан с учетом инструментов, и что легче понять мысленно порядок, в котором события происходят в запросе, потому что они идут в том же порядке лексически.
Эрик Липперт
2
Эрик, с точки зрения программирования декларативного стиля, я понимаю, что Linq подходит (скажем, читаемый), чем SQL. Но является ли он более читабельным, чем SQL? Ни за что. Если «собака ест мясо красное» трудно, насколько легче «из кухни, где красный цвет достать нож»? На самом деле мы все говорим и думаем как «достань тот нож, который лежит на столе на кухне». И именно здесь SQL ближе к реальной жизни, чем LINQ.
Nawfal
6
Я хочу чашку кофе из Starbucks, где магазин открыт до полуночи. Вам это знакомо? Это в том же порядке, что и SQL-запрос. Да, LINQ может быть более читабельным, чем ненужный код, который вы должны сгенерировать для выполнения стандартного запроса SQL, но LINQ не более читаем, чем сам запрос SQL. Так да; разработчикам LINQ может быть выгодно работать слева направо, но как для меня это важно? Меня волнует только удобство использования.
KyleM
8
@KyleM: конечно; удобство использования было очень важным аспектом дизайна LINQ. В частности, быть поддающимся инструментам повышения производительности было ключевым. Поскольку область видимости течет слева для записи в запросе LINQ, к тому времени, когда вы вводите, c.среда IDE уже знает тип cи может дать вам IntelliSense. В LINQ вы говорите «от c в клиентах, где c.» и бум, вы получаете IntelliSense, помогая вам, перечисляя членов Customer. В SQL, когда вы набираете «ВЫБРАТЬ имя из клиентов», вы не можете получить помощь от IDE, чтобы сказать вам, что «имя» - хороший выбор, потому что вы печатали name раньше customers .
Эрик Липперт
22

Как и все остальное в мире программирования, вы должны привыкнуть к синтаксису, и тогда его (потенциально) легче читать.

Как и все остальное в мире программирования, существует потенциал для спагетти-кода или других злоупотреблений.

Как и все остальное в мире программирования, вы можете сделать это так или иначе.

Как и все остальное в мире программирования, ваш пробег может варьироваться.

Вонко вменяемый
источник
6

Я увидел комментарий / замечание, в котором он что-то сказал - в отношении LINQ / lambda - в духе: «Пишите код, читаемый людьми, а не читаемый вашим компьютером».

Я думаю, что это утверждение имеет много достоинств, однако рассмотрим разработчика (такого как я), который прошел через весь спектр языков разработки от ассемблера, через процедурный, через ОО, через управляемый, используя высокопроизводительные параллельные решения для задач ,

Я гордился тем, что сделал свой код максимально читабельным и многократно используемым, и принял многие из принципов шаблонов проектирования GOF, чтобы предоставлять системы и сервисы качества производства во многих различных бизнес-секторах.

Когда я впервые столкнулся с лямбда-выражением, я подумал: «Что это, черт возьми, ?!» Это было сразу нелогично моему знакомому (и потому удобному) явному декларативному синтаксису. Младшие <5 лет на работе, ребята, однако, сложили это, как будто это была манна небесная!

Это потому, что в течение многих лет мыслить как компьютер (в синтаксическом смысле) очень легко переводилось в синтаксис прямого кодирования (независимо от языка). Если у вас было такое вычислительное мышление в течение примерно 20 лет (в моем случае 30+), вы должны понимать, что первоначальный синтаксический шок лямбда-выражения может легко перерасти в страх и недоверие.

Может быть, сотрудник в ОП произошел от меня, похожего на меня (то есть несколько раз был в этом районе), и это было для них нелогичным в то время? Мой вопрос: что вы с этим сделали? Пытались ли вы переучить своего сверстника в понимании преимуществ встроенного синтаксиса, или вы осудили / осудили его за то, что он «не участвует в программе»? Первый, вероятно, видел бы, как ваш коллега пришел к вашему образу мышления, а второй, вероятно, заставил бы их еще больше не доверять синтаксису LINQ / лямбда и, таким образом, усугубил бы негативное мнение.

Для себя я должен был перевоспитать свой собственный образ мышления (как выводит Эрик выше, это не незначительное изменение ума, и мне пришлось программировать в Миранде в 80-х, так что у меня был свой опыт функционального программирования) но как только я прошел через эту боль, преимущества были очевидны, но - что более важно - там, где его использование было чрезмерно использовано (то есть использовалось ради его использования), более сложным и повторяющимся (учитывая принцип СУХОЙ в этом случае).

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

Так что, когда кто-то говорит, что они "не получают лямбду?" или синтаксис LINQ, вместо того, чтобы рекламировать их, пытаться помочь им понять основные принципы. В конце концов, у них может быть «старая школа», как у меня.

ВКК
источник
Интересный комментарий - у меня было это, когда я переключался со строго типизированных, статических языков на динамические языки. Мне потребовалось некоторое время, чтобы приспособиться (страх и недоверие), и теперь мне трудно вернуться, честно.
lunchmeat317
4

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

Это лучше со смесью двух .

Напишите это в Lambda, если это быстрее (вам нужно, чтобы это было быстро) или легче для чтения.

Амир Резаи
источник
Да, но что бы сделать его Linq / LAMDA не легче читать?
BlackICE
извините, значит не легче читать, чем альтернатива.
BlackICE
1

Я думаю, что в большинстве случаев это зависит (кроме случаев, когда вы делаете что-то очень странное) от того, имеете ли вы в виду «читабельность», когда кто-то понимает, что происходит, или он может легко найти все крошечные мелочи.

Я думаю, что ссылка помогает с первым, но часто (особенно при отладке) вредит второму.

ИМХО, когда я смотрю на код, я незнаком с первым, что гораздо важнее второго, поэтому я нахожу его гораздо более читабельным.

Билл
источник
Хорошая точка зрения. Последний обычно покрывается хорошим модульным тестом.
Скотт Уитлок
Таким образом, вы думаете, что внутренняя часть оценки linq затрудняет поиск всех деталей? Можете ли вы привести пример, когда это может быть?
BlackICE
3
@David: выражения Linq оцениваются не просто лениво, а выражениями . Принимающий код выражения linq может на самом деле изменить выражение, поэтому он делает что-то совершенно другое, например макрос lisp, работающий с s-выражениями. Например, при работе с linq to SQL он на самом деле берет выражение where e.FirstName == "John"и переводит его в запрос SQL! Он делает это, просматривая не скомпилированное (но анализируемое) выражение, сравнивая свойство, вызываемое FirstNameдля сохраняемой сущности, и сравнивая со строкой и т. Д. Многое происходит.
Скотт Уитлок
@ Давид, как правило, я не нахожу детали, которые трудно найти, пока вы не начнете использовать linq против себя. «Простой» пример этого, который занял у меня много времени, заключался в следующем: bugsquash.blogspot.com/2008/07/y-combinator-and-linq.html, но в некоторых есть много более обязательных примеров из того, что люди делают с выражениями в областях dynamic, DynamicObject и IDynamicMetaObjectProvider. То, где вы рисуете линию относительно того, что такое linq, является дискуссионным, но, как указывает Скотт, все это доступно для / в linq, потому что linq использует те же деревья выражений.
Билл
@ Хорошо, я скажу тебе, что это сложно, но функции y-комбинатора и старшего порядка повредят большинство наших голов, это как рекурсия, понимаешь ты это или нет. Легче ли читать рекурсивную реализацию (если вы не знали ни того, ни другого)?
BlackICE
1

Я нахожу синтаксис LINQ интуитивно понятным и легким для чтения, тем более что они помещают FROM в начале, где он принадлежит, а не в середине, как в SQL. Но лямбды IMO сбивают с толку и затрудняют чтение кода.

Мейсон Уилер
источник
Как вы думаете, почему лямбды сбивают с толку? Это вывод типа?
ChaosPandion
1
@ChaosPandion: сначала вывод типа, а затем символ. Соединение = и a> выглядит как>>, что кто-то напортачил и написал задом наперед, и это заставляет мой мозг зацикливаться.
Мейсон Уилер
@Mason - Вам нравится синтаксис Haskell ( \x -> x * x) или F # ( fun x -> x * x)?
ChaosPandion
@ChaosPandion: Нет, не особенно. Лямбды могут быть быстрыми для написания, но я нахожу их трудными для анализа, особенно когда они становятся нетривиальными по сложности. Ваши примеры не так уж плохи, но они, как правило, становятся намного хуже.
Мейсон Уилер
6
@ Мейсон: Похоже, вы возражаете против оскорблений ламдаса, а не против идеи ламдаса в первую очередь.
Анон.
1

Я согласен с вами, что синтаксис Linq существенно не отличается от T-SQL. Я думаю, что ваш коллега, возможно, действительно возражает против смешения реляционных вещей, в которых его красивый и блестящий ОО-код. С другой стороны, функциональное программирование требует некоторого привыкания и готовности к нему.

Ларри Коулман
источник
0

Это зависит. Очевидно, T-SQL однозначно предоставляет некоторые DB-реляционные решения. Очевидно, что LINQ однозначно предоставляет некоторые ОО-решения.

Тем не мение; "более запутанным, чем T-SQL?" - обсуждается / задается в первоначальном вопросе. Это явно подразумевает некоторые особенности, которые не рассматриваются ни в одном из существующих ответов, вместо этого обвиняя (очевидно, знакомого с SQL) критику в том, что он застрял в прошлом.

Поэтому, хотя я ценю LINQ за определенные качества и не сильно не согласен с существующими здесь ответами, я считаю, что контрапункт заслуживает представления:

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

from c in categories
join p in products on c equals p.Category into ps
from p in ps.DefaultIfEmpty()

Если вы думаете, что это интуитивно, больше силы для вас. ;)

Shannon
источник
-4

Вероятно, есть причина, по которой Linq отстой, просто попробуйте сделать примеры из реального мира вместо примеров из учебников.

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

Правда в том, что LinqLambda. В некоторых случаях они очень полезны, и считается, что они не заменяют всю изящную объектную ориентацию, которую вы, ребята, никогда не понимали.

    public IList<Customer> GetVipCustomers(IList<Customer> source, int maxCount)
    {
        var results = new SortedList<string,Customer>();

        foreach (Customer c in source)
        {
            if (maxCount == results.Count)
                break;

            if (c.IsVip && c.FirstName== "Aaron" || c.SecondName== "Aaron")
                results.Add(c.LastName, c);
        }

        return results.Values;
    }
Марк
источник
6
Я думаю, что source.Where(c => c.IsVip && c.FirstName == "Aaron" || c.SecondName == "Aaron").Take(maxCount).OrderBy(d => d.LastName);твое, но твое трудно читать, поэтому я мог ошибиться;)
JK.
1
Как вы считаете, что это примеры из учебников? Это на самом деле код производственного использования. Было бы полезно, если бы вы предоставили для сравнения и ваш классический «читаемый» код, и версию linq / lamda, вместо того, чтобы ожидать, что все его конвертируют.
BlackICE
3
Вы зарегистрировались специально, чтобы пожаловаться на LINQ?
КЧалу
1
@KChaloux Я думаю, что они были просто троллингом, если честно
jk.