А мне больше по душе окислительно-восстановительные реакции. Так что давайте встретимся втроём. Обычно на троих всё легче понимается.
“Облегчения работы мусорщика” может быть только по сравнению с чем то. Раз лямбды это сахар над обычными подпрограммами - это сравнение не может быть с этими подпрограммами (ибо они равны). А с чем ещё сравнивать - не представляют.
Если программа пишется на максимальной производительности - лучше наоборот избавляться от лямбд. Особенно тех, что захватывают переменные. Хоть JIT и неожиданно хорош в оптимизации лямбд, если нужна вся производительность - контролировать захват переменных надо самостоятельно, своими классами-контейнерами.
Когда такой контроль не нужен - в приоритет становится чистота программы. То есть если вы используете какую то подпрограмму 1 раз - это лямбда.
А ещё лямбды не обязательно куда либо передавать. Из них получаются прекрасные вложенные подпрограммы. При чём в отличии от обычных вложенных подпрограмм - они ещё и захватывают переменные:
begin
var MyFunc1: (real, string)->string := (x,s)->
begin
Writeln((x,s));
Result := x.ToString(s);
end;
// P значит Percent
MyFunc1(123.456, 'P').Println;
end.
Тут MyFunc1
будет видна только в теле begin-end
, где объявлена. Это одно из мощнейших средств инкапсуляции (в данном случае - скрытия видимости того, что не должно использоваться во вне) в паскале.
“Окис-востас” мы на первом курсе проходили реально за три занятия )))
А вот почему сульфаты меди и железа кристаллизуются именно в пяти- и семиводные купоросы соответственно, толком так и не объяснили.
И диаграмму “железо-углерод” учили на манер “Отче наш” …
В общем, любой предмет требует изучения, а “Физика за 12 часов”," Паскаль за 2 часа", “Электротехника за три дня” - это лохотроны.
А скажите, это откуда у вас такие слухи?
Что касается памяти:
begin
var a, b, c: byte;
var p1: Action0 := ()->
begin
Writeln(a, b);
end;
var p2: Action0 := ()->
begin
Writeln(b, c);
end;
end.
Обычно лучше когда для p1
и p2
создаётся 1 общий контейнер.
А иногда важно чтоб когда p1
удалена, а p2
ещё используется - сбощик мусора мог удалить a
. С лямбдами это невозможно.
Что касается скорости:
procedure p0(var a: byte);
begin
...
end;
begin
var a: byte;
var p: Action0 := ()->
begin
p0(a)
end;
Writeln(a);
end.
Лямбда захватывает и возможно (нельзя знать т.к. передали var
-параметром) изменяет a
. Значит после лямбды - нужно использовать версию a
из контейнера.
Но a
из контейнера - это поле класса, хранимое в куче. А что если мы знаем что лямбда точно не изменяет a
и нам нужна переменная со стека?
Ну, в отличии от предыдущего случая - это хотя бы реализуемо:
procedure p0(var a: byte);
begin
...
end;
begin
var a: byte;
var captured_a := a;
var p: Action0 := ()->
begin
var a := captured_a;
p0(a)
end;
Writeln(a);
end.
Вот только, получается, нам пришлось засорить пространство имён именем, которое нельзя использовать. Это плохо.
Поэтому имеет смысл объявить свой класс-контейнер:
procedure p0(var a: byte);
begin
...
end;
type
//ToDo имя, релевантное для ситуации
Container1 = auto class
a: byte;
procedure p;
begin
p0(a);
end;
end;
begin
var a: byte;
var p := Container1.Create(a).p;
Writeln(a);
end.
Конечно оба примера - это случаи чрезвычайно тонкого контроля. Но об этом и шла речь.
А скажите, доктор, откуда у Вас такие картинки?
Это понятно. Но это странное утверждение.
Вы говорите, вот лямбды есть в языке. Но не используйте их - это неэффективно.
Что-то здесь не так.
Более того, сильно не так в треде где мы специалисту в области химии пытаемся объяснить, что такое лямбда-выражения и как и зачем их использовать.
В моё время были программисты-ассемблерщики, которые возмущались по любому поводу если программа была написана на языке высокого уровня, и требовали переписать её на ассемблер ради эффективности.
И ещё были те, кто сразу писал в машинных кодах, и они говорили, что ассемблер не всегда один в один переводит в машинные коды, от чего снижается эффективность.
Вы всё про своё… Я же не зря это добавил
@John_Nada сказал что думал “использовать лямбды это для особых случаев”, а я ответил “НЕ использовать лямбды - вот что для особых случаев”.
Понятно. Ну - не сбивайте человека.
- может и неэффективно, но всё равно вместо одной Select, к примеру, не сделаешь 100 на разные случаи жизни.
Эффективно это когда используешь функцию “Замесить” и “Нарубить”, и их выполняют правильно, а не как двое из ларца, которые ожидают что-то вроде вовка.тесто.замесить и вовка.дрова.нарубить
Ничего не понял )
По моему имелся в виду такой ужас:
{$region Select'ы}
function Select_Sqr(self: sequence of integer): sequence of integer;
begin
foreach var i in self do
yield i.Sqr;
end;
function Select_SqrSqr(self: sequence of integer): sequence of integer;
begin
foreach var i in self do
yield i.Sqr.Sqr;
end;
{$endregion Select'ы}
Не пишите ужасы в этом треде пожалуйста.
Ну я к тому, что чем ближе к естественному языку , тем меньше времени на обучение программированию. Но, к сожалению, больше времени другим программистам на реализацию функции “Замесить”.
В этом плане лямбда-выражения “неэффективны”. Но зато можно сделать один Select , а не много “ужасов” типа Select_Sqr.
На самом деле .Select
не эффективен только как метод Linq
. Сами колбеки хорошо оптимизируются.
Когда то сам тестировал и впал в ступор когда увидел результат: 0.pas (1,1 КБ)
(не забудьте отключить отладку и запускать через Shift+F9, чтоб включилась оптимизация)
По-моему, химик уже выпал в осадок.
Вы так всех людей лямбдами распугаете.
Хорошо, берем простейшую задачу.
Дан целочисленный массив, длина которого не превышт 10 000, а значения
элементов по абсолютное величине не превышают 99. Найти и вывести
сумму положительных элементов массива, оканчивающихся цифрой 7.
Вот три варианта решения. Первый - без функций, второй - с внешней функцией и третий с “лямбдой”.
begin
var a := ArrRandom(ReadInteger, -99, 99);
a.Println;
var s := 0;
foreach var k in a do
if (k > 0) and (k mod 10 = 7) then s += k;
Print(s);
end.
function Good(t: integer) := (t > 0) and (t mod 10 = 7);
begin
var a := ArrRandom(ReadInteger, -99, 99);
a.Println;
var s := 0;
foreach var k in a do
if Good(k) then s += k;
Print(s);
end.
begin
var a := ArrRandom(ReadInteger, -99, 99);
a.Println;
a.Where(t -> (t > 0) and (t mod 10 = 7)).Sum.Print
end.
Я весь внимание: расскажите, пожалуйста, какая из трех программ “ближе к естественному языку” и на этом основании “требует меньше времени на обучение” ?
В продолжение темы. Сравните код второй и третьей программы. В третьей цикл вместе с “if” превратился в Where, описание параметра функции в t, а тело функции переписано без изменения. Соединив знаком -> параметр и тело функции, мы получили эту непонятную лямбду-страшилку. Или, если посмотреть и подумать - не такую уж непонятную и совсем не страшилку?
Ну вот почему настолько часто на форуме появляются утверждения, на которые приходится писать очередной “Наш ответ Керзону”?
По моему под “естественным языком” - имелось в виду что то типа машины Поста. Или, прекрасного идейного наследника - брейнфака.
@Andrej_Rulin это частое ошибочное представление. Под “легко научиться” надо понимать не “легко знать все возможности” а “легко научиться использовать”.
Понять и запомнить принцип брейнфака можно меньше чем за минуту, без особой подготовки. А чтоб научится писать на нём Hello World
- надо хотя бы пару часов.
В то же время под .Net - никто не изучает принцип работы маршлинга объектов типа класса в первый (а часто и в пятый) год. Зато написать консольную игру можно вы первый день.
Химия наполнена функциями. В теории цвета - там просто чудовищные по виду функции, которые вызывают оторопь у студентов. Да даже простой и попсовый потенциал водородного электрода рассчитывается по достаточно сложной формуле Нернста.