[C# 3.0] синтаксис лямбда выражений

6yrop

var locals =
customers
.Where(c => c.ZipCode == 91822)
.Select(c => new { FullName = c.FirstName + “ “ + c.LastName,
HomeAddress = c.Address });

раз уж упрощали синтаксис, зачем аргументы делегата в начале определять? Сделали бы какую-нибудь плюшечку, чтобы помечать аргумент при первом использовании. А то это напоминает Паскаль-стиль — определяем локальные переменные в начали метода и только тогда их можно использовать
var locals =
customers
.Where(=>@c.ZipCode == 91822)
.Select(=>new { FullName = @c.FirstName + “ “ + c.LastName,
HomeAddress = c.Address });

alfadred

А то это напоминает Паскаль-стиль
Это напоминает запись лямбда-выражений [math]$$\lambda x. x^2$$[/math], которая от Черча пошла.

timefim

Если больше одной переменной?

6yrop

Если больше одной переменной?
нет проблем
 

.Where(=>@c.ZipCode == 91822 && @dealer.Name == "test")

salora

и чем твоя запись лучше? имхо, она сосёт. Особенно в случае нескольких переменных, когда неизвестен порядок аргументов. А то, как это сделано в питоне (func(arg1 = val1, arg2=val2, ... не подходит под C-стиль

Alexander08

собачка уже зарезервирована для других целей ;) :grin:

salora

бугага!

6yrop

и чем твоя запись лучше? имхо, она сосёт
тем что ты "определяешь" переменные по требованию.
Чисто визуальное в первом варианте "с" употребляется два раза, в моем один. Число символов совпало, но это из-за того что используется односимвольная переменная, что в реальности редкость.

salora

читай дальше, только что добавил

6yrop

да, че та про порядок переменных я забыл :o Тогда вопрос снимается...

ppplva

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

Dasar

> А многобуквенные имена в однострочнках - дурной стиль
однобуквенные имена, в любом случае, дурной стиль
имхо, так намного лучше бы смотрелось

var locals =
customers
.Where(customer => customer.ZipCode == 91822)
.Select(customer => new { FullName = customer.FirstName + “ “ + customer.LastName,
HomeAddress = customer.Address });

ppplva

Серьезно ? Никогда не называл переменные x и y ? В функции на 5 строк как называешь переменную цикла ?
for (int i=0;i<10;++i) customers[i].blabla;

Неужели customer_index, customerId или еще как-нибудь позаковыристее ?

Dasar

для этого foreach есть

6yrop

Никогда не называл переменные x и y ?
да..., с таким стилем надо поосторожнее, у нас одного чувака уволили, из-за того что он вот так переменные называл. Конечно, его уволили не только из-за этого, и иногда такие обозначения все таки используются, но это особенные случаи.

vall

ну аргументы определять всё-равно надо если их >1
другое дело что если он один могли-бы завести дефолтовое имя типа it как в groovy (или откуда они это слизали? из руби?)

6yrop

ну аргументы определять всё-равно надо если их >1
тип аргументов теперь выводится, остается только порядок определять. Фактически назначаются имена номерам мест аргументов, и это делается при каждом использовании, что немного раздражает.

ppplva

Издеваешься ? Не в каждом языке есть, не для любых структур данных применим. Или принципиально не веришь в циклы ?
Матрицы приходилось перемножать ? Неужели номер строки и столбца называешь как-то длиннее чем i, j или x, y ?
Допустим, нужно умножить каждый элемент массива на 2: lambda x: x*2 или lambda item_price: item_price * 2 ?

Dasar

Матрицы приходилось перемножать ?
в матрице x,y - нормально, если матрица однородная
i,j - хуже, т.к. у каждого разработчика свои предпочтения, i - по вертикали, или j - по вертикали
> Допустим, нужно умножить каждый элемент массива на 2: lambda x: x*2 или lambda item_price: item_price * 2 ?
каждый элемент массива чем является? так и называть.

kokoc88

т.к. у каждого разработчика свои предпочтения, i - по вертикали, или j - по вертикали
Всегда думал, что i, j при перемножении матриц выбирают как в математике. :crazy:

Dasar

> Всегда думал, что i, j при перемножении матриц выбирают как в математике.
фразы про математику хороши, пока народ параллельно, и работает, и в институте учится.
а лет через 10 после института уже хрен вспомнишь где там была i, а где j.

kokoc88

а лет через 10 после института уже хрен вспомнишь где там была i, а где j
Если человек забыл, где там i, а где j, то он уж точно не помнит, как перемножать матрицы. Поэтому он посмотрит где-нибудь математический алгоритм, в котором они будут на правильном месте.

Helga87

Совсем необязательно

Dasar

Всегда думал, что i, j при перемножении матриц выбирают как в математике
если матрицы именно математические, то ты прав
если же матрицы не математические, то возможны варианты

kokoc88

если же матрицы не математические, то возможны варианты
Ну если не математические, то большинство сначала запишет i, потом j; выбрав сначала строку, а затем столбец...

ppplva

каждый элемент массива чем является? так и называть.
map(lambda item_price: item_price * 2, item_prices)
или
map(lambda x: x*2, item_prices) ?
Второй вариант мне кажется ничуть не менее понятным, а в первом от item_price в глазах рябит.

Dasar

скорее
map(lambda price: price * 2, prices)
и хорошо видно, что на что умножается
ps
что полезного в item_ я не понял

rosali

эх когда уже в вашем C# будет хаскельное
map (*2) prices
...

Dasar

map (*2) prices
а как тогда записывается?
map (price: price^2 + price) prices

Olenenok

map (\price=>price^2+price) prices

freezer

для этого foreach есть
иногда в теле цикла бывает нужен номер ;)
А кстати, foreach для массивов до сих пор медленнее исполняется, чем for?

Alexander08

иногда в теле цикла бывает нужен номер
вот это уже действительно плохой стиль проганья! в перечесляемом объекте должны быть все его свойства!

Olenenok

иногда в теле цикла бывает нужен номер ;)
циклы не нужны

freezer


string[] names = new string[] {"Вася", "Петя", "Маша", "Путин", "John Dow"};
System.Array.Sort(names);
for(int i=0; i<names.Length; ++i)
writer.WriteLine("{0}. {1}", i, names[i]);

как такой же результат получить без счётчика? :smirk:

freezer

там где "не нужны" циклы, их роль играет рекурсия

vall

names=["Вася", "Петя", "Маша", "Путин", "John Dow"]
names.sort
for i, name in enumerate(names):
print "%d. %s" % (i, name)

kruzer25

Ты сначала сформулируй задачу, а потом её решай.
Я вот тут вижу "распечатать строки, при этом, указав перед каждой строкой номер вывеенной строки". Если ч0, это делается как-то так:
$iNameNum = 1;
foreach($names as $name) {
printf('%d. %s', $iNameNum, $name);
$iNameNum++;
}

UPD: А лучше - как-то так:
class StringsIterator {
private $arrStrings;
public function __construct(array $arrStrings) {
$this->arrStrings = $arrStrings;
}

private $iStringNum;
private function printString($string) {
printf('%d. %s', $this->iStringNum, $string);
$this->iStringNum++;
}
public function printAll {
$this->iStringNum = 1;
array_map(/*$this->printString($_1)*/array($this, "printString" $this->arrStrings);
}
}

:p

freezer

C# 3.0 это не скомпилирует :smirk:

vall

это её личные проблемы =)

freezer

Я вот тут вижу "распечатать строки, при этом, указав перед каждой строкой номер вывеенной строки". Если ч0, это делается как-то так:
Правильно, так оно тоже делается, но тут мы параллельно с foreach всё равно заводим счётчик (iNameNum). Для структур типа списков, у которых нет прямого доступа к элементам, только так и получится. А для массивов это - извращение

kruzer25

Что для тебя означает "массив"?
Просто ты тут завязываешься на то, что индекс - это действительно то, что тебе нужно. А ключи, на самом деле, могут быть какими угодно.

freezer

вы бы ещё HTML в пример привели с его <ul><li>

kruzer25

Кстати, да.
<ul>
<foreach $strings as $string>
<li><=$string></li>
</foreach>
</ul>

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

freezer

Массив - это то что по-англицки array. В нашем случае - одномерный, с типом элемента string.
Твой вариант на C# выглядел бы так:

int i=0;
foreach(string name in names)
{
writer.WriteLine("{0}. {1}", i, name);
++i;
}

внутрях реализовывалось бы это как-то так:

int i=0;
for(IEnumerator<string> en = names.GetEnumerator; en.MoveNext; ++i)
writer.WriteLine("{0}. {1}", i, en.Current);

в общем, сомневаюсь, что читабельность или производительность от этого выше будет.

freezer

то есть, тут уже проблема не в стиле программирования, а в заказчике. Ему надо так и сказать: "Чувак, нафига тебе отчёты в txt? Давай мы тебе HTML замутим, а то придётся счётчики для циклов заводить..." :lol:

vall

ну это ещё можно решить специальным writer`ом, что даже логично, раз нумерация является не свойством объектов а состоянием вывода.

kruzer25

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

freezer

Враппер, который считает и нумерует строки? :crazy:
Воистину, мы не ищем лёгких путей :grin:

kruzer25

Воистину, мы не ищем лёгких путей
Лёгких - для чего?
Лёгких - для написания и понимания, что тут делает вот эта строчка?
Или для облегчения работы оптимизатора, чтобы подсунуть ему уже готовую реализацию, чтобы он там никакие сложные foreach-и не распутывал, или, не дай боже, с объектами начинал работать?

freezer

Читабельность - однозначно будет лучше
Однозначно ли? Я вот не уверен. Может попросим модераторов голосовалку замутить?

kruzer25

Ну попроси.
В моём случае, всё очевидно - что имели в виду, то и написали.
А в твоём - хуйня какая-то выходит.

freezer

Лёгких - для написания и понимания, что тут делает вот эта строчка?
Для написания - гораздо проще взять любой из for/foreach, чем париться со специализированным writer'ом, отлавливающим конец строки, который может быть и не понадобится-то больше никогда ;)

kruzer25

Ну, я так понял, у тебя эта хуйня не в одном месте используется?

freezer

А в твоём - хуйня какая-то выходит.
дожили... простейший сишный for-цикл у программистов уже трудности вызывает :(

kruzer25

Не вызывает никакие трудности, только зачем писать плохо, если можно написать хорошо?
Ты ещё скажи "простейший ассемблерный набор инструкций уже вызывает трудности, дожили".

timefim

Можно сделать так
 
public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource, int> action)
{
int i = 0;
foreach (var item in source)
{
action(item, i++);
}

}


names.ForEachname, index) => ...);

freezer

это называется без счётчика?
Тут как ни крути, а счётчик всё равно будет. Пусть даже в виде параметра у рекурсивной функции.

Dasar

> А кстати, foreach для массивов до сих пор медленнее исполняется, чем for?
это кто сказал?
в C# 2.0 foreach часто выполняется для массивов быстрее, чем for, т.к. jit-у проще понять где можно выкинуть проверки на корректность индекса.

Dasar

внутрях реализовывалось бы это как-то так:
неправда, для массива внутрях будет так:

int i=0;
for(int j = 0; j < names.Length; ++j)
{
writer.WriteLine("{0}. {1}", i, names[j]);
++i;
}

timefim

Это называется без for.

kruzer25

Неправда, или ключи массива - это обязательно все целые числа от 0 до n?

Dasar

если массив, то правда
для ассоциативного массива (Dictionary) - так уже не будет

freezer

это кто сказал?
в C# 2.0 foreach часто выполняется для массивов быстрее, чем for, т.к. jit-у проще понять где можно выкинуть проверки на корректность индекса.
да уж не помню кто сказал, читал где-то. Возможно, в 1.1 так и было.
Сейчас посмотрел что c# 3.0 генерит, действительно, в случае с foreach заводится два параллельных счётчика. в остальном код одинаковый, элемент из массива достаётся одним и тем же ldelem.ref, то есть JIT-у что одно, что другое - всё равно, если умный, то второй счётчик должен выкинуть

aleks058

Дядюшка Билл уже все сделал за нас:

string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };

var shortDigits = digits.Wheredigit, index) => digit.Length < index);

Console.WriteLine("Short digits:");

foreach (var d in shortDigits)
Console.WriteLine("The word {0} is shorter than its value.", d);

Взято отсюда

bleyman

C# 3.0 это не скомпилирует
Это серьёзно её личные проблемы.
Теоретически можно написать что-нибудь вроде
 
  
public class IndexedEnumerator<T> : IEnumerable<KeyValuePair<int, T>>
{
private IEnumerable<T> inner;

public IndexedEnumerator(IEnumerable<T> inner)
{
this.inner = inner;
}

public IEnumerator<KeyValuePair<int, T>> GetEnumerator
{
IEnumerator<T> e = inner.GetEnumerator;
for (int i = 0; e.MoveNext; i++)
{
yield return new KeyValuePair<int, T>(i, e.Current);
}
}


System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator
{
return GetEnumerator;
}

[STAThread]
public static void Main(string[] args)
{
string[] zzz = { "a", "b", "c" };
foreach (KeyValuePair<int, string> kvp in new IndexedEnumerator<string>(zzz
{
MessageBox.Show("ItemId = " + kvp.Key + " Item = " + kvp.Value);
}
}
}

(с поправкой на возможность писать "var" вместо "KeyValuePair<int, string>" в третьем шарпе).
И даже, наверное, работать будет не намного медленней. Но это, блин, реальная проблема шарпа, что такого класса нет в стандартной библиотеке. Даже если закрыть глаза на то, что туплы (ака кортежи) и групповое присвоение в общем-то полезны на уровне языка.
Я даже расширю утверждение и заявлю, что это проблема всех ориентированных на программистов продуктов микрософта.
У питона правильный подход, "batteries included".
У жавы плохой, но терпимый подход, "ебитесь сами", в результате образуется несколько (но не много!) конкурирующих в-некотором-смысле-стандартных библиотек, в каждой из которых всё есть, но совокуплять их тяжело и вообще.
У микрософта отвратительный подход, "не нужно отбирать у программистов работу", вроде бы как есть _почти_ всё, но нет кучи мелочей (некоторые из которых весьма фундаментальны, вот как эта в результате программисты либо тратят жизнь на написание "for (int i = 0; i < zzz.Count; i++)", либо каждый пишет свою личную кривую, глючную и тормозную VasyaPupkin.Utils, которую тянет за всеми своими прогами.
Это очень, очень плохо и грустно!

freezer

тогда уж так :o
static class EnumerateExtension
{
public static IEnumerable<KeyValuePair<int, T>> Enumerate<T>(this IEnumerable<T> sequence)
{
int i = 0;
foreach (T value in sequence)
{
yield return new KeyValuePair<int, T>(i, value);
++i;
}
}
}
class Program
{
static void Main
{
string[] names = new string[] { "Вася", "Петя", "Маша", "Путин", "John Dow" };

foreach (var x in names.Enumerate
Console.WriteLine("{0}. {1}", x.Key, x.Value);
}
}

P.S. туплы (анонимные типы) же появились в 3.0

aleks058

Появиться-то они появились, только от них толку как от козла молока.
Анонимный тип метод вернуть не может - придется object возвращать, а в вызывающем коде к пропертям через Reflection лазить.
Бесполезная штука, короче.

6yrop

а в вызывающем коде к пропертям через Reflection лазить.
к слову: например ASP.NET датабайндинг именно через рефлекшен вызывает свойства (WPF вроде тоже). Причем никого это как бы не замечает, везде только пишут вот какая удобная штука датабайндинг... . Вот нашел сообщение в блоге
http://www.dotnetjunkies.com/WebLog/joshuagough/archive/2006...
Там что-то про ASP 3.0, но аспнет уже вышел, ничего такого я там не увидел, все по старому Eval/Bind.

6yrop

да Microsoft вообще отжигает. Проделать такую работу по созданию LINQ, а потом предлагать пользователю писать это все в маркапе как стринг
 

<asp:LinqDataSource
ContextTypeName="AdventureWorksDataContext"
TableName="Products"
Where="ListPrice > @SelectedPrice"
Select="new(Name, Size, StandardCost, ListPrice, DaysToManufacture)"
ID="LinqDataSource1"
runat="server">
<WhereParameters>
<asp:ControlParameter
Name="SelectedPrice"
DefaultValue="0"
ControlID="DropDownList1"
Type="Int32" />
</WhereParameters>
</asp:LinqDataSource>

 

<asp:LinqDataSource
ContextTypeName="ExampleDataContext"
TableName="Products"
GroupBy="new(ProductCategory,Color)"
Select="new(Key,
Average(ListPrice) as AverageListPrice,
Count as RecordCount)"
ID="LinqDataSource1"
runat="server">
</asp:LinqDataSource>

А вы говорите рефлекшен.... :crazy:

freezer

зачем возвращать, зачем рефлексия? Вот, всё просто, даже проще чем с foreach:
    static class EnumerateExtension
{
public delegate void Proc<T>(int key, T value);
public static void Enumerate<T>(this IEnumerable<T> sequence, Proc<T> proc)
{
int i = 0;
foreach (T value in sequence)
proc(i++, value);
}
}
class Program
{
static void Main
{
string[] names = new string[] { "Вася", "Петя", "Маша", "Путин", "John Dow" };

names.Enumeratekey, value) => Console.WriteLine("{0}. {1}", key, value;
}
}

Dasar

names.Enumeratekey, value) => Console.WriteLine("{0}. {1}", key, value;
хуже, потому что отличается от стандартного foreach: например, break, continue, return уже не вставишь в тело цикла.

freezer

В Linq-выражениях тоже нет ни break, ни continue... Максимум что можно - интервал вырезать

Alexander08

что именно тебе не нравится/возмущает?

freezer

А вот кстати такой вопрос... Есть ли способ сериализовать Expression<>, пусть даже с некоторыми ограничениями?

6yrop

что именно тебе не нравится/возмущает?
LINQ создавали чтобы иметь строгую типизацию. А то можно просто писать сиквел стайтменты и получать DataTable-ы, причем родной сиквел для выбранной СУБД это даже круче, поскольку ты можешь использовать особенность конкретной СУБД напрямую.
Используя запросы LINQ в маркапе, убивает всю строгую типизацию, проверку на этапе компиляции и т.д.

6yrop

А вот кстати такой вопрос... Есть ли способ сериализовать Expression<>, пусть даже с некоторыми ограничениями?
где-то читал, что в текущей реализации сериализовать нельзя, и за это их ругали

freezer

реально нужно... :(
Похоже, придётся вручную делать.

6yrop

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

freezer

так замыкания можно вычислить на момент сериализации и сохранить в качестве значения, меня бы такой вариант устроил.

aleks058

Идея интересная, спасибо.
Но все-таки постоянно заниматься таким "разворотом мозга" напряжно, да и значения иногда хочется вернуть так, чтобы получатель строго типизированно мог с ними работать, а класс новый описывать лень.
Кстати, вопрос. Может ли анонимный класс реализовывать интерфейс. Это было бы очень кстати.
Не понимаю, почему нельзя было хотя бы сделать так:

public interface IMyClass
{
new { int, string } GetValue; // Так описывать анонимные классы в интерфейсе
}

public class MyClass : IMyClass
{
public var GetValue // Реализация метода из интерфейса
{
return new { Num = 4, Str = "Four" };
}

public var GetAnotherValue // Компилятор может определить тип возвращаемого выражения
{
return new { Num = 5, Str = "Five" };
}

public new { int, string } GetYetAnotherValue // Зададим тип явно
{
return new { Num = 6, Str = "Six" };
}
}

При этом считать, что два анонимных класса имеют одинаковый тип, если у них одинаковые свойства (типы, имена, порядок).

6yrop

 
Появиться-то они появились, только от них толку как от козла молока.
Анонимный тип метод вернуть не может - придется object возвращать, а в вызывающем коде к пропертям через Reflection лазить.
  

Они вот эти и предлагают пользоваться, подписываясь на событие LinqDatasource.Selecting

http://weblogs.asp.net/scottgu/archive/2007/09/07/linq-to-sq...
Правда, сверху в этой же статье есть замечание
 
Note: you do not need to write your query expression in-line within the event handler. A cleaner approach would be to encapsulate it within a helper method that you just call from the event handler. I show how to create one of these helper methods in the beginning of my Part 8 blog post (using a GetProductsByCategory helper method).
  

Короче, MS в своем стиле :grin: .
И такое пишет General Manager within the Microsoft Developer Division, афигеть ....

6yrop

Интересно в ReSharper-e сделают герелку — из анонимного класса генерировать и подставить именованный класс?

durka82

Еще интересно, когда они его выпустят?..

Alexander08

че вы так на анонимусы взъелись? чего вам не хватает?
получаете анонимный класс,

List<int> lMass = new int[] { 1, 2, 3 }.ToList<int>

var @var =
from m in lMass
where m > 1
select new {m, sm = m.ToString m5 = -m};

а потом делаете с ним что хотитие

var @varlist = @var.ToList;
@varlist.Sorta1, a2) => a1.m5.CompareTo(a2.m5;

var @vardic = @var.ToDictionary(a => a.m);
@vardic.Add(1, new { m = 1, sm = "", m5 = 1 });
foreach (var varelem in @var)
{
...
}

или конвертируете в нужную структуру данных и возвращаете

return new KeyValuePair<int, string>(@var.First.m, @var.First.sm);
return @var.ToDictionary(a => a.m, a => a.sm);
return @var.ToLookup(a => a.m, a => a.sm);

вроде оч удобная штука!

durka82

Что же они всегда так тянут :(
Ведь и VS уже давно вышел, а про беты я уж и не говорю.
Хотя бы выпускали патчи для 2005-го решейпера, чтобы он мог работать с 2008-м...

6yrop

или конвертируете в нужную структуру данных и возвращаете
нужная структура это IEnumerable<?> :grin: , где ? — название анонимного класса

6yrop

Хотя бы выпускали патчи для 2005-го решейпера, чтобы он мог работать с 2008-м...
не, там движок сильно переделывать надо

Alexander08

ну если тебе нужен такой класс - напиши. в чем проблема?

freezer

typeof(IEnumerable<>).MakeGenericType(typeof(@var
:grin:

6yrop

ну если тебе нужен такой класс - напиши. в чем проблема?
класс и так уже есть, у него просто нет имени. А имя тоже особо не нужно, оно требуется только, чтобы передать из метода наружу коллекцию.
А проблема в том, что это сложнее мейтайнить, например, авто-промерти ввели по той же причине — меньше кода проще майнтейнить.
Оставить комментарий
Имя или ник:
Комментарий: