Code style, использовать ли переменные?
// неужели не понятно, что в разных случаях получается по разному?
неужели не понятно, что в разных случаях получается по разному?Ну вот и хочется узнать конкретнее об этих случаях.
Тут есть мнение, что имя временной переменной служит как бы комментарием к тому, что в ней лежит - для случаев, когда это непонятно из контекста. Но, как мне кажется, из контекста (и из имён аргументов других методов) это будет понятно практически всегда.
TypeOfX
чё за херь?
Не бойся, я не имел в виду макросы. Просто имя класса, такое же, как SomeClass.
тогда, на мой взгляд, вот это SomeFunc(x => x.DoSomething лучше. В этой записи не упоминается имя класса, оно выводится автоматически. Я все больше и больше проникаюсь какая крутая вещь вывод типа.
да, первый вариант предпочтительнее. Второй вариант считаю плохим стилем, он может быть оправдан только, если производился формальный рефакторинг. Кстати, ReSharper делает преобразования в обе стороны от одной записи к другой.
static SomeType DoSomething(SomeOtherType arg) {да, это ты увлекся, это уже ковнокод получаетca — вводится лишняя сущность — метод Tee.
return new SomeClass(arg.something.PassDebug.DoSomething.Tee(obj => obj.UpdateSomething.something;
}
тогда, на мой взгляд, вот это SomeFunc(x => x.DoSomething лучше. В этой записи не упоминается имя класса, оно выводится автоматически.Это - плюс. А вот все бесконечные x => x.*** - имхо, минус.
да, первый вариант предпочтительнее. Второй вариант считаю плохим стилемМожно поконкретнее?
x => x.*** - имхо, минус.а что тут лишнего? это "x => x."? да, возможно, и можно было бы сократить до ".", но, имхо, это не особо принципиально, привыкаешь. Когда лишними являются четко повторяющиеся конструкции, то к такому привыкаешь и глаз уже пропускает такое. А вот, если нужно писать и читать имя класса это уже действительно лишняя инфа и поэтому напрягает.
Чтобы к TResult f(T1 arg1, T2 arg2, T3 arg3) можно было обратиться и как к Func<T1, TResult> f(T2 arg2, T3 arg3 и как к Func<T1, T2, TResult> f(T3 arg3) (Func<T1, T2, T3, TResult> f уже есть, это просто f без скобок); ну и то соответствие между динамическими и статическими методами, о котором я уже сказал (т.е. чтобы можно было в любом классе (необязательно статическом) можно было написать статический extension для данного класса метод).
Первое решается и руками, но это задолбаешься для каждого количества аргументов писать по 2*n extension-методов curry (для func и для action)... да и явно типизированные к своим типам (ну вроде как Predicat<T> вместо Func<T, bool>) делегаты придётся явно кастовать в Func-и и Action-ы...
Когда лишними являются четко повторяющиеся конструкцииНу так они не чётко повторяются, ты не можешь написать x => x => x.DoSomething(x надо писать x => y => y.DoSomething(x).
О, я понял, чего мне не хватает.Смени язык на тот, что поддерживает карринг. А также где можно определять control flow операторы типа |> и т.п. F# тот же.
поэтому такая ботва
return new SomeClass(arg.something.DoSomething.something;
идет лесом
а такую
var x = arg.something;
var y = x.DoSomething;
var z = new SomeClass(y);
var u = z.something;
return u;
можно свернуть до такой
var y = arg.something.DoSomething;
var z = new SomeClass(y);
return z.something;
Ну так они не чётко повторяются, ты не можешь написать x => x => x.DoSomething(x надо писать x => y => y.DoSomething(x).да, автоматический карринг не помешал бы. Может и введут когда-нибудь.
имхо надо чтобы весь код нормально читался либо сверху вниз, либо слева направо.чё та я не проникся глубоким смыслом этого правила. Я и первый вариант легко читаю слева на право.
ну как бы к объекту arg.something.DoSomething применяется сначала приведение (операция стоит от него слева а потом взятие поля - операция уже стоит справа от него
new SomeClass(
arg.something.DoSomething
).something;
т.е.выражения вида f(g(h(x принципиально плохо читаются?
Я бы не сказал, что они читаются сильно лучше, чем x.h.g.f
как раз нормально читается слева направо
а вот если так: f(g(h(x).s).y) то плохо
апд:второй вариант пенартура в однобуквенной буквенной нотации превращается вf(a.s.d.x
может это и нормально, но мне режет глаз
что они читаются сильно лучше, чем x.h.g.fда, но они позволяют вводить несколько аргументов у любой функции, и при это все аргументы симметричны, а как твою (extention) запись обобщить на случай многих аргументов?
а как твою (extention) запись обобщить на случай многих аргументов?В теории - { x, y }.f
Просто имеет смысл определиться, какую запись использовать - слева направо или справа налево. А сейчас приходится в части случаев писать x.f а в части - f(x).
а вот если так: f(g(h(x).s).y)Ты просто идеальную иллюстрация моего примера запостил: x.h.s.g.y.f.
{ x, y }.fтогда уж (x, y)f
но, на самом деле, справа налево это плохо читается — сначала идет формирование пары (x, y а потом мы видим зачем мы эту пару формируем (чтобы передать в функцию f). Классическая запись f(x, y) приятнее — сначала видно, что идет вызов функции f, затем идет формирование для нее аргументов.
сначала идет формирование пары (x, y а потом мы видим зачем мы эту пару формируем (чтобы передать в функцию f).Так же можно сказать и про обычное x.f
Речь не о том, что лучше, подход "слева направо" или подход "справа налево", а о том, что любой из них лучше подхода "в 50% случаев слева направо, а ещё в 50% - справа налево".
Так же можно сказать и про обычное x.fне скажи, здесь x.f у нас есть объект и мы для объекта вызываем метод. А вот если аргумента два, то мы формируем пару и потом вызываем функцию. Так вот хочется сначала видеть, зачем мы начинаем формировать пару. Т.е. в случае одного аргумента перестановка аргумента и имени функции не влияет на читаемость, а если число аргументов больше одного, влияет.
не скажи, здесь x.f у нас есть объект и мы для объекта вызываем метод. А вот если аргумента два, то мы формируем пару и потом вызываем функцию. Так вот хочется сначала видеть, зачем мы начинаем формировать пару.А если x заменяем на x.y?
Чем принципиально отличается x.y.f от {x, y}.f? Точно так же - "хочется сначала видеть, зачем мы начинаем вызывать метод y у x".
static TResult _<TResult>(this Func<TResult> func) {
return func;
}
static TResult _<T1, TResult>(this T1 arg1, Func<T1, TResult> func) {
return func(arg1)
}
...
и тогда можно будет вместо
{x, y}.f
писать
{x, y, f}._
Но это уже было
хочется сначала видеть, зачем мы начинаем вызывать метод y у xне знаю как ты, а я вызываю метод затем, что этого требует задача, которую я программирую. А вот, потребность формировать пару возникает из-за способа записи вызова функции с двумя аргументами. Формирование пары это фактически начало записи вызова функции, и, на мой взгляд, лучше сначала прочитать название функции
Запись f(g(h(x разворачивается в цепочку без вложенных скобочек x.f.g.h только при одном аргументе. Оптимально как раз смешивать право и левосторонние стили
— правосторонний стиль позволяет избавиться от вложенные скобочки для функций с одним аргументом, что является плюсом. Но имя функции стоит не на первом месте в записи вызова функции с несколькими аргументами, что является минусом.
— для левостороннего стиля ситуация обратная.
Запись вызова функции при правостороннем стиле
— для функций одного аргумента “.f”. Видим, что запись начинается почти с имени функции. Точка не мешает, поскольку это символ, который не зависит от вызываемой функции.
— для нескольких переменных “{..., ..., ..., ... ...}.f”. Тут уже в начале идет формирование набора аргументов, а мы еще не знаем, зачем мы формируем этот набор.
Тут уже в начале идет формирование набора аргументов, а мы еще не знаем, зачем мы формируем этот набор.Ещё раз. А если у нас в первом случае ("функция одного аргумента") этот "один аргумент" записывается не одной буквой, а тоже каким-то сложным выражением? Получается то же самое - формируем аргумент, а ещё не знаем, зачем.
var x = DoSomething(arg);
var y = f(x);
var z = g(x);
return h(y, z);
можно, ценой говнокода, обойтись без этой отдельной строчки:
return (x => h(f(x g(oSomething(arg;
Тут уже нихрена не понять, что происходит, но, если немного модифицировать (нынешний язык такого не позволяет то выйдет терпимо:
return arg.DoSomething.(x => h(f(x g(x;
или, после ещё одного преобразования:
return arg.DoSomething.(x => { f(x g(x) }).h;
или, совсем сильно фантазируя:
return { arg.DoSomething f, g }.Calculate.h;
(правда, хрен напишешь автокомплишен, который с таким безумием сможет работать, так что для ООП это, наверное, всё-таки неприменимо).
Получается то же самое - формируем аргумент, а ещё не знаем, зачем.а вот и не тоже самое, сложное выражение для аргумента формируется, исходя из поставленной задачи, а вот пару формируем из-за способа записи.
а вот пару формируем из-за способа записиЧем отличается {x, y} от x.f?
В лиспе вот, вроде бы, именно такая запись используется, и особо никто не жалуется.
Чем отличается {x, y} от x.f?тем, что вложенные скобочки {{x, y}, z} читаются хуже, чем x.y.z
В лиспе вот, вроде бы, именно такая запись используется, и особо никто не жалуется.возможно, у записи {{x, y}, z} есть плюсы, которые перекрывают указанный минус. Я пока не в курсе.
{x, y} от x.f?кстати, ты тут опечатался или нет?
кстати, ты тут опечатался или нет?Нет - имеются в виду не конкретные случаи, а вообще.
возможно, у записи {{x, y}, z} есть плюсы, которые перекрывают указанный минус. Я пока не в курсе.В лиспе афаик вместо фигурных скобочек - круглые, а запятых нет. Перекрывают?
UPD: Вру, в лиспе функция идёт в начале, а не в конце... хотя, если подумать - всё это условности, наверное, f(x) ничем не отличается от x(f). Какая разница, sqrt(4) или 4(sqrt)?
тем, что вложенные скобочки {{x, y}, z} читаются хуже, чем x.y.zЧем хуже?
Ну и не забывай, что в моей выдуманной нотации для функций с одним аргументом можно скобочки не писать, нужна в них возникает только при написании функций от нескольких аргументов.
Ср.В телепатическом режиме предполагаю, что лучшим не является "самый короткий код".
code:
static SomeType DoSomething(SomeOtherType arg) {
var x = arg.something;
var y = x.DoSomething;
var z = new SomeClass(y);
var u = z.something;
return u;
}
vs
code:
static SomeType DoSomething(SomeOtherType arg) {
return new SomeClass(arg.something.DoSomething.something;
}
Какой вариант лучше (конечно, при условии, что все временные переменные используются по одному разу)?
Лучший вариант виден в конкретике. В каких-то случаях предпочтителен один вариант, а в каких-то другой. Включив воображение, я все же в 90% случаях выбрал бы первый вариант и обязательно дал бы осмысленные имена переменным вместо x, y, z. Тогда полученный код настроен на легкую модификацию и расширение в будущем.
Во втором варианте и дебажить можно:Так как код приходится менять, то второй вариант я бы назвал "недебугельным". Первый вариант гораздо (даже слишком) debugger-friendly.
я бы назвал "недебугельным".на самом деле, в 95-99% случаев под дебагом можно посмотреть столько же инфы и для второго способа записи
var a = xxx.DoSomething;
Debugger.Debug(a);
a.DoSomething;
и
xxx.DoSomething.PassDebug.DoSomething;
bool isCurrentDirectorySystem = (Enviroment.CurrentDirectory == Environment.SystemDirectory);
if (isCurrentDirectorySystem)
...
Я сейчас начинаю предпочитать второй вариант (если документировать переменными нет необходимости но немного измененный (верю и надеюсь, что он помогает дебагить - даёт доп. инфу о строке с исключением,- но не проверял).
В предельном случае будет как-то так:
static SomeType DoSomething(SomeOtherType arg)
{
return new SomeClass
(
arg
.something
.DoSomething
)
.something;
}
Оставить комментарий
kruzer25
Ср.vs
Какой вариант лучше (конечно, при условии, что все временные переменные используются по одному разу)?
Во втором варианте и дебажить можно:
и, в принципе, даже допускается совершение каких-то промежуточных действий с объектами (хотя и выходит говнокод):
И ещё пришло в голову - почему на уровне языка (C#) нет фичи, чтобы методы объекта являлись и методами класса, принимающими на вход объект? То есть, чтобы
работало, как
?
Тогда можно было бы вместо всяких SomeFunc(x => x.DoSomething писать просто SomeFunc(TypeOfX.DoSomething)
Хотя, выходит, даже лучше, чтобы статический метод выглядел как-то так:
и можно будет писать вместо SomeFunc(x => x.DoSomething('xxx' писать SomeFunc(TypeOfX.DoSomething('xxx'