Чи призводить використання LINQ та Lambda Expressions до менш читабельного коду? [зачинено]


43

Я маю дискусію з колегою по Linq, я скопію тут:

Співробітник: Нехай тут буде чесно. Синтаксис Linq смокче. Це заплутано і неінтуїтивно.

Я: давай, більш заплутаний, ніж T-SQL?

Співробітник: е, так.

Я: у неї однакові основні частини, виберіть, де і звідки

Співробітник: Linq, на мою думку, є принадністю реляційних + OO. Співробітник: Не зрозумійте мене неправильно - це надзвичайно потужно, але вони переробили SQL для використання об'єктних колекцій.

Я вважаю, що використання Linq + Lamda's дуже потужне (він погоджується), а також полегшує читання коду (він не згоден з цього приводу):

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 sucks". Ось що говорить ваш колега. Я думаю, що дебати в іншому місці перестали.
Скотт Вітлок

48
Чи турбує когось ще, що люди схильні вживати слово "інтуїтивно", коли вони насправді означають "знайоме"?
Ларрі Коулман

7
У будь-якому випадку команда C # (включаючи Еріка Ліпперта) пішла з шляху, щоб пояснити, що Linq не є переносом SQL, вона була розроблена з нуля, як і більшість інших функцій. Мені б сказати, що ваш колега - Люддіт.
Aaronaught

16
Для чого це варто: я просто змусив свою дружину (адміністратор офісу - поруч із нульовим досвідом практичного програмування) переглянути 3 приклади aaronaught і зміг розшифрувати наміри прикладів linq та lambda набагато простіше, ніж традиційний імперативний приклад.
Стівен Еверс

Відповіді:


73

Я більше не можу знайти правильну посаду, але Ерік Ліпперт (і, можливо, кілька інших програм) вже кілька разів замислювався про те, як Лінк декларативно , що для кількох класів проблем набагато інтуїтивніше, ніж імперативний синтаксис.

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 до SQL, через вуду дерева виразів та розумний дизайн команди C #, що робить анонімні функції неявно перетворюваними на дерева виразів.


6
+1 Якщо вам не подобається LINQ, це, мабуть, тому, що "ви все робите не так" :)
Darknight

1
Linq is purely object-oriented+1 для цього. Ось чому наш посібник зі стилю команди застосовує вільний синтаксис над синтаксисом запиту. Я вважаю, що робить характер зв'язку OO більш очевидним для тих, хто використовував для об'єкта позначення на мовах, подібних С.
СБІ

1
Я знаю, що посаді 7 років. Але ось питання: чи використовували ви функціональні мови як свій головний інструмент до зустрічі з Linq?
Сергій.quixoticaxis.Ivanov

4
Відверто кажучи, я віддаю перевагу циклу foor, тому що я одразу бачу, де ми можемо і матимемо виключення з NULL-посиланнями, як обробляється null, і це не відкладене виконання, яке робить налагодження важким, і він не створює n списків також, так що цикл for-циклу також є більш ефективним.
Четвер

1
@Aaronaught, Якщо ви видалите сортування та переформатуєте процедурний код у стилі Хорстмана , я скажу, що він є більш зрозумілим, ніж версія 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
Нітпік: Німецькою мовою "Der Hund frisst das Fleisch" - це насправді той самий порядок, що й англійською мовою, "Собака їсть м'ясо" :) Окрім цього, я знайшов синтаксис запитів LINQ дуже читабельним, як тільки зрозумів, що це не SQL .
Майкл Штум

18
@gbjbaanb: Я не виправдовую синтаксис LINQ на цілком суб'єктивній основі, оскільки він є більш зрозумілим . Швидше я виправдовую це на цілком об'єктивній основі, що набагато простіше розробити інструменти, які допомагають користувачеві в побудові запитів LINQ, оскільки синтаксис LINQ був розроблений з урахуванням інструментальних засобів і що простіше розумово зрозуміти порядку, в якому події трапляються в запиті, оскільки вони в одному порядку лексично входять.
Ерік Ліпперт

2
Еріко, з точки зору програмування декларативного стилю я розумію, що Linq є більш доцільним (я вважаю, читабельним), ніж SQL. Але чи читати це людськіше, ніж SQL? У жодному разі. Якщо "собака їсть м'ясо червоне" важко, наскільки простіше "з кухні, де червоний колір отримує ніж"? Насправді ми всі розмовляємо та думаємо на кшталт «дістаньте той ніж, який стоїть на столі з кухні». І саме там SQL ближче до реального життя, ніж LINQ.
nawfal

6
Я хочу чашку кави з зіркових баб, де магазин відкритий до півночі. Це вам звучить знайомо? Це в тому ж порядку, що і 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 / lambda і тим самим посилювати негативну думку.

Для себе мені довелося перевиховати власний спосіб мислення (як Ерік підказує вище, це не незначна зміна розуму; мені довелося програмувати в Міранді в 80-х, щоб я мав свою частку досвіду функціонального програмування) але як тільки я пережив цей біль, переваги були очевидні, але, що важливіше - там, де його використання було застосовано (тобто використовувалося заради його використання), над складним та повторюваним (зважаючи на принцип DRY у тому випадку).

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

Тож коли хтось каже, що вони "не отримують лямбда?" або синтаксис LINQ, а не називати їх луддитською спробою, щоб допомогти їм зрозуміти основні принципи. Зрештою, вони можуть мати "стару школу", наприклад, я.


Цікавий коментар - у мене це було, коли я перейшов із сильно набраних, статичних мов до динамічних. Мені довелося трохи налагоджуватися (страх і недовіра) і тепер мені важко повернутися назад, чесно.
обідне м’ясо317

4

Lambda вирази призводять до менш читаного коду, якщо запити занадто довгі. Однак це набагато краще, ніж занадто багато вкладених петель .

Краще із сумішшю двох .

Напишіть це в лямбда, якщо це швидше (потрібно, щоб він був швидким) або легше читався.


Так, але що б це не зробило linq / lamda не легшим для читання?
BlackICE

вибачте, мав на увазі не легше читати, ніж альтернативне.
BlackICE

1

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

Я думаю, що посилання допомагає першим, але часто (особливо при налагодженні) шкодить другому.

ІМХО, коли я дивлюся на код, я незнайомий з першим, це набагато важливіше, ніж останній, тому я вважаю його набагато більш зрозумілим.


Влучне зауваження. Останній, як правило, охоплюється хорошим одиничним тестом.
Скотт Вітлок

Отже, ви вважаєте, що внутрішня оцінка оцінювання ускладнює пошук усіх деталей? Чи можете ви навести приклад, коли це може бути?
BlackICE

3
@David: вирази Linq не просто ледачі, вони вирази . Код прийому виразу linq може фактично змінювати вираз, тому він робить щось зовсім інше, як макрос lisp, що працює над s-виразами. Наприклад, працюючи з linq в SQL, він фактично приймає вираз where e.FirstName == "John"і переводить його в SQL-запит! Це робиться так, дивлячись на некомпільований (але проаналізований) вираз, бачачи, що це порівняння властивості, викликаної FirstNameперсистуючою сутністю, і порівняння з рядком тощо. Багато чого відбувається.
Скотт Вітлок

@david Взагалі мені не важко знайти деталі, поки ви не почнете використовувати linq проти себе. "Простий" приклад цього, який зайняв мені тривалий час, щоб загорнути голову, ось такий: bugsquash.blogspot.com/2008/07/y-combinator-and-linq.html, але в деяких випадках обов'язково є багато прикладів речей, які люди роблять із виразами у динамічній, DynamicObject та IDynamicMetaObjectProvider областях. Де ви малюєте лінію щодо того, що таке linq, це дискусійне питання, але як вказує scott, всі ці речі доступні для / in linq, оскільки linq використовує ті самі дерева виразів.
Білл

@ Добре, я скажу вам, що це важко, але y-комбінатор і функції високого порядку завдадуть шкоди більшості наших голов, це як рекурсія, ви отримаєте це чи ні. Чи легше читати рекурсивну реалізацію цього (якщо ви не знали жодної)?
BlackICE

1

Я вважаю синтаксис LINQ інтуїтивно зрозумілим і легким для читання, тим більше, що вони ставлять ВІД на початку, куди він належить, а не в середині, як у SQL. Але лямбди IMO плутають і ускладнюють читання коду.


Чому, на вашу думку, лямбди плутають? Це типовий умовивід?
ChaosPandion

1
@ChaosPandion: По-перше, висновок типу, а по-друге, символ. Якщо скласти разом an = і a>, схоже на> =, що хтось накрутив і написав назад, і це, як правило, кидає мій мозок на цикл.
Мейсон Уілер

@Mason - Вам подобається синтаксис Haskell ( \x -> x * x) або F # ( fun x -> x * x)?
ChaosPandion

@ChaosPandion: Ні, не особливо. Лямбди можуть швидко написати, але мені важко розібратися, особливо коли вони стають нетривіальними за складністю. Ваші приклади не такі вже й погані, але вони, як правило, набагато гірші.
Мейсон Уілер

6
@ Mason: Це здається, що ви заперечуєте проти зловживань над lamdas, а не заперечуєте в першу чергу проти ідеї Lamdas.
Анон.

1

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


0

Це залежить. Очевидно, T-SQL однозначно надає деякі БД-реляційні рішення. Очевидно, LINQ однозначно надає деякі рішення OO.

Однак; "більш заплутаний, ніж 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()

Якщо ви думаєте, що це інтуїтивно, більше сили для вас. ;)


-4

Мабуть, є причина, чому Linq смокче, просто намагайтеся робити приклади з реального світу замість цих прикладів з підручника.

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

Правда, LinqLambdaThings дуже корисні в небагатьох випадках, і не вважається, що вони замінять усі чудові орієнтації об'єктів, які ви, хлопці, ніколи не розуміли

    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?
KChaloux

1
@KChaloux Я думаю, що вони просто тролінгували, якщо чесно
jk.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.