Zero, у тебя какая-то концептуальная путаница.
Нет, моя позиция четко очерчена. Ты же, напротив, почему-то считаешь, что если императивный подход скрыт за фасадом библиотеки, то он перестает таковым быть. Но это не так, почему - я поясню ниже частями, отвечая на твои выпады.
Ты зачем-то прицепился к последовательности действий при вызове лямбд внутри filter и утверждаешь что оно имеет какое-то значение. Какое значение оно имеет? Для конечного результата оно не имеет никакого значения. Можно написать
filter(name -> name.startsWith("J")).filter(name -> name.length() > 6)
или
filter(name -> name.length() > 6).filter(name -> name.startsWith("J"))
и в конце ты получишь одно и то же, что очевидно.
Нет-нет-нет, я не держусь за это, это мелкая частность.
Пусть для ясности останется только один фильтр, никакой разницы - моя позиция та же. Пусть у нас будет
.filte(name->name.length() >6).map.collect.
Поменять место map ты естественно не можешь, потому что в этом случае ты меняешь ЧТО ты хочешь получить. Так же как в SQL есть разница между
....
Ну и collect() вызывается всегда в конце чтобы сигнализировать что ты закончил формулировать ЧТО ты хочешь и теперь желаешь получить результат. Это как semicolon в SQL запросе, только в Java ты еще указываешь в каком виде вернуть результат.
Не совсем. Точка с запятой и в Java будет той же, collect, насколько я понимаю имеет иную цель (могу ошибаться - не мое поле). map мы не можем поставить в иное место, потому что он работает по результатам фильтра, императивно. И collect нам указывает, что результаты предыдущих шагов (фильтр+мап) мы сливаем с некий sink, который будет у нас target коллекцией. Это вполне императивное описание.
Если бы мы имели дело с декларативным подходом, мы вызывали бы некую условную машину (как, например, Пролог машину, бразуер, читающий CSS или СУБД) и сказали бы ей следующее в некоторой форме, синтаксис не важен:
"Сделай так, чтобы была у меня target коллекция, в которой будут все элементы из изначальной коллекции, которые попадают под условие filter и эти элементы должны быть upcase'нуты".
И все. Как там эта машина будет это делать - а Х её З, это ее дело. Нам по барабану, как СУБД наш SQL запрос спланирует и исполнит, мы декларативно результат описали.
И отличие, проверка на вшивость декларативности подхода в данном случае следующее: машина, которая выполняет такой запрос (см. в кавычках выше) гипотетически может выполнить его так:
1) Скопировать всю source коллекцию в target коллекцию
2) Итерироваться по ней
3) Подходящие элементы upcase'нуть
4) Неподходящие - удалить.
5) Profit.
Да, О() может быть хуже, но не суть. Должна быть гипотетическая возможность такого развития событий. А раз мы указываем, что мы итерируемся по source а потом сливаем в target, то мы вполне себе императивно указываем алгоритм действий. Так же, как если бы напрямую писали for...if итд, только используем stream нотацию. Опять же - если я верно ее понял.
Он - нет. Но он сидит внутри библиотеки, ты его не видишь. А твой код, который использует эту библиотеку, может уже быть декларативным, а не императивным. Возьмем SQL. Ты наверное понимаешь, что там внизу, близко к железу, он превращается в последовательность машинных команд - в императивный код. Но ты пишешь декларативный SQL. Точно так же и со стримами в Java - я пишу какой результат мне нужен - в декларативном стиле
Это некорректная аналогия. Мы сравниваем в данном случае язык SQL запросов как язык и Java как язык.
Соответственно в обоих случаях есть что-то под капотом - анализатор запросов, планировщик и пр. в случае SQL или транслятор в байт-код, виртуальная машина, преобразование в машинный код и так далее. Но мы сравниваем два фасада - SQL запросы и Java. А ты пытаешься библиотеку в Java поставить на один уровень с кишками СУБД, это некорректно. Для мира SQL запросом "библиотекой" (ее аналогией) бы был, например, подзапрос.
Ну конечно. Это супердекларативный подход. Сделай то, не знаю что, но дай мне правильный, ожидаемый мной результат. Чистая декларативщина. Правда, пока компьютеры не стали сверхинтеллектуальными, возможный в реальности декларативный подход будет такой как в SQL или в Stream'ах.
Ок, давай я по шагам напишу преобразования такого кода в псевдокоде, а ты напиши, на какой же итерации код из императивного превратился в декларативный, ок? Все условно, формальные параметры - сквозные.
Раз:
func main
Collection lSource;
Collection lTarget;
<get data into Source here somehow>
for each lItem in lSource
if(length(lItem) < 6) and BeginsWith(lItem, "J")
lTarget.Add(UpCase(lItem));
end
end
Два:
Collection func processCollectionSequentially(Collection ASource)
Collection lTarget;
for each lItem in ASource
if(length(lItem) < 6) and BeginsWith(lItem, "J")
lTarget.Add(UpCase(lItem));
end
return lTarget;
end
func main
Collection lSource;
Collection lTarget;
<get data into Source here somehow>
lTarget = processCollectionSequentially(lSource);
end
Три:
LFilter = bool func(string AItem);
LProcess = string func(string AItem);
Collection func processCollectionSequentially(Collection ASource,
LFilter AFilterLambda,
LProcess AProcessLambda)
Collection lTarget;
for each lItem in ASource
if AFilterLambda(lItem)
lTarget.Add(AProcessLambda(lItem));
end
return lTarget;
end
func main
Collection lSource;
Collection lTarget;
<get data into Source here somehow>
lTarget = processCollectionSequentially(lSource,
func(
return (length(AItem) < 6)
and BeginsWith(AItem, "J")
),
func(
return upCase(AItem);
));
end
Четыре:
Модуль 1
LFilter = bool func(string AItem);
LProcess = string func(string AItem);
Collection func processCollectionSequentially(Collection ASource,
LFilter AFilterLambda,
LProcess AProcessLambda)
Collection lTarget;
for each lItem in ASource
if AFilterLambda(lItem)
lTarget.Add(AProcessLambda(lItem));
end
return lTarget;
end
Модуль 2
use Модуль 1
func СделайЗаепца()
Collection lSource;
Collection lTarget;
<get data into Source here somehow>
lTarget = processCollectionSequentially(lSource,
func(
return (length(AItem) < 6)
and BeginsWith(AItem, "J")
),
func(
return upCase(AItem);
));
end
Модуль main
func main
СделайЗаепца();
end
Нет, если этот товарищ так считает, то это лишь демонстрирует его безграмотность. Естественно, ничего принципиально нового в Java-ских лямбдах с точки зрения теории нет, но практическая их реализация и то как они выглядят в байткоде отличается от использования анонимных классов. Они не являются только лишь синтаксическим сахарком. По соображениям производительности для них потребовалось менять JVM. За подробностями, см, например, https://www.infoq.co...-Under-the-Hood.
Нет, это ты не понял. При чем тут джавовские анонимные классы и как оно выглядит в байт-коде? Речь о том, что сам подход был еще с тех времен, когда никакой Java не было. Да, я читал и смотрел лекции о Java lambdas under the hood, но не о том было сказано. Этот подход нов для Джавы только потому, что стратегия "все суть классы и ООП - пророк его" не катит всегда - приходится писать бойлерплейт как ты показал выше с Override'ами. И лямбды - это шаг к процедурному подходу (технически).
Сообщение изменено: Zero (21 ноября 2017 - 03:02 )