Страницы

Поиск по вопросам

Показаны сообщения с ярлыком лямбда-выражение. Показать все сообщения
Показаны сообщения с ярлыком лямбда-выражение. Показать все сообщения

воскресенье, 8 марта 2020 г.

Имплементация функционального интерфейса

#java #дизайн_языка #лямбда_выражение


Понимание интерфейса, как конструкции языка, до некоторых пор было мною вроде бы
как усвоено. Столкнувшись с лямбда-выражениями это понимание рискует быть пересмотренным.
Что я имею ввиду? 

Интерфейс позволяет "объединить" в некотором смысле относительно разные классы и
работать с ними под определенным углом (интерфейсом). Так или иначе, чтобы им (интерфейсом)
воспользоваться, его необходимо имплементировать.

Функциональные интерфейсы, которые определяют один метод, ведут себя иначе? Их не
нужно имплементировать, достаточно создать ссылку на них и присвоить этим ссылкам лямбда-выражение? 

Мне интересен сам масштаб возможностей, который здесь открывается. Выходит просто
ссылка на функциональный интерфейс внутри любого класса способна манипулировать объектами
любого класса?
    


Ответы

Ответ 1



Просто как пример, что же такое лямбда в Java Лямбда new AccidentsRequest(result -> {if (!result.has("error")) parseJSON(result);}, true); ТРАХ-ТИБИДОХ, АХАЛАЙ-МАХАЛАЙ!!! И лямбда превращается... В анонимный класс! new AccidentsRequest(new AsyncTaskCompleteListener() { @Override public void onTaskComplete(JSONObject result) throws JSONException { if (!result.has("error")) Content.this.parseJSON(result); } }, true); Фактически лямбды в Java - это такой синтаксический сахар для анонимных классов с одним методом.

пятница, 28 февраля 2020 г.

Почему java воспринмает интерфейс comparator как функциональный

#java #интерфейс #lambda #лямбда_выражение


Недавно ознакомился с лямбда-выражениями и узнал, что с их помощью можно реализовать
Comparator. Но мне не даёт покоя то, что в этом интерфейсе объявлено два не статических
и не реализованных по умолчанию метода: compare() и equals(). И несмотря на это, интерфейс
считается функциональным, и метод compare() может быть реализован с помощью лябда-выражений. 

Как так? Почему метод equals() не воспринимается?
    


Ответы

Ответ 1



Интерфейс Comparator функциональный, потому что equals() - относится к public-методу класcа Object. Интерфейсы объявляют абстрактные методы относящиеся к public методам Object, но обычно делают это неявным образом и эти методы не учитываются.

пятница, 14 февраля 2020 г.

LINQ из выражения в точечную нотацию

#c_sharp #net #linq #лямбда_выражение


Есть классы моделей 

public class Product
{
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public string Category { get; set; }
    public decimal UnitPrice { get; set; }
    public int UnitsInStock { get; set; }
}

public class Order
{
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public decimal Total { get; set; }
}


Необходимо получить всех клиентов, у которых были заказы, превосходящие по сумме
величину value

Я сделал следующий запрос 

       var customers = from c in dataSource.Customers
                        from o in c.Orders
                        where o.Total > value
                        select c).Distinct();


Выводит то что нужно. 

Необходимо выполнить данный запрос в точечной нотации, я пытаюсь сделать так: 

        var customers = dataSource.Customers.Select(c => c.Orders
                                                        .Where(o => o.Total > value))
                                                        .Distinct();


и получается, что запрос возвращает  IEnumerable>, а мне необходим
IEnumerable. 
Скажите пожалуйста в каком месте я ошибаюсь, и как делать запросы подобного вида
правильно. 
    


Ответы

Ответ 1



Вам нужно написать так: var customers = dataSource.Customers.Where(c => c.Orders.Any(o => o.Total > value)).Distinct(); Если нужно чтобы хотя бы один заказ больше value, или так: var customers = dataSource.Customers.Where(c => c.Orders.All(o => o.Total > value)).Distinct(); Если нужно чтобы все заказы больше value. В данном случае не стоит использовать метод Select, так как он возвращает новую коллекцию.

Ответ 2



Два from подряд, в query форме соответствуют методу SelectMany Запрос может принять вид: var customers = dataSource.Customers .SelectMany(c => c.Orders, (c,o) => new {c, o}) .Where(item => item.o.Total > value) .Select(item => item.c) .Distinct();

среда, 29 января 2020 г.

Лямбда выражения и функторы в стандартных алгоритмах

#cpp #алгоритм #лямбда_выражение


Опишите пожалуйста плюси и минусы  вариантов  для конкретно этого примера(применение
в std::for_each) , и когда и почему лямбда имеет преимущество(или наоборот)  по отношению
к функтору:

 ...
void f(const int value)
{
    cout << value << endl;
}

int main()
{    
    vector v{1, 2, 3, 4, 5};
    function op(&f) ; // функтор
    auto p = [](const int i) { return f(i + 5); }; // лямбда
    for_each(v.begin(), v.end(), op); // первый вариант
    for_each(v.begin(), v.end(), p);   // второй вариант
    ...
}

    


Ответы

Ответ 1



В качестве вишенки :) Создаю вектор и иже с ним: void f(int value) { sum += value; } const int Count = 100000000; vector v(Count); generate(v.begin(),v.end(),g); function op(&f); Затем просто суммирую в глобальную переменную: for_each(v.begin(),v.end(),f); for_each(v.begin(),v.end(),[](const int i) { sum += i; }); for_each(v.begin(),v.end(),op); VC++2017 дал на моей машине следующие времена (в мс) - 187, 41 и 208 соответственно. Ideone дает примерно те же результаты. P.S. Явное создание struct Sum { void operator()(int value) { sum += value; } }; как и следовало ожидать, от лямбды ничем не отличается.

Ответ 2



Ну, во-первых, в такой ситуации вы можете передавать в алгоритм прямо сразу f. Никакой оболочки вокруг f из лямбды или из std::function тут формально не требуется. (Разумеется, если вы хотите прибавить 5 к аргументу, то лямбда - именно то, что надо.) Во-вторых, при создании, удалении и на каждом вызове std::function тянет за собой накладные расходы, связанные с затратами на type erasure внутри std::function. Поэтому передавать std::function лучше только туда, где на входе требуется именно std::function. std::function предназначен в первую очередь для устранения зависимости принимающего алгоритма от шаблонного параметра, описывающего тип функтора. В вашем случае, т.е. для std::for_each, никакой речи о таком устранении не идет: в std::for_each тип функтора уже присутствует в списке шаблонных параметров, никто его не устранял и устранять не собирается. И поэтому платить накладные расходы за std::function нет никакого смысла. Лямбда тут будет работать эффективнее. Это относится ко всем стандартным алгоритмам: у них у всех типы функторов, предикатов, компараторов и т.п. вынесены в список параметров шаблона. В такой ситуации нет смысла использовать std::function без каких-то дополнительных/посторонних на то причин.

Что значит :: знак в java? [дубликат]

#java #lambda #java_stream #лямбда_выражение


        
             
                
                    
                        
                            На этот вопрос уже дан ответ здесь:
                            
                        
                    
                
                        
                            Что значит оператор ::?
                                
                                    (1 ответ)
                                
                        
                                Закрыт 2 года назад.
            
                    
Часто вижу :: при определении нового экземпляра, в стримах, иногда в лямда выражениях.
Что он значит? Что значит List :: stream?

P.S. И как он называется?
    


Ответы

Ответ 1



Оператор "ссылка на метод" (Method Reference). По сути - сокращенная запись для ссылки на метод из функционального интерфейса при использовании лямбды. List::stream является ссылкой на метод stream() из интерфейса List List> list = Arrays.asList( Arrays.asList(1,2,3), Arrays.asList(4,5,6) ); list.steam() .flatMap(List::stream) .forEach(System.out::println); приведенный отрывок кода означает: 1. Мы инициализируем поток, в котором будет список чисел (Stream>) 2. с помощью flatMap мы объединяем список чисел в поток чисел (Stream) 3. с помощью forEach и ссылки на метод println для каждого элемента из потока вызывается вывод на экран Если владеете английским, хорошее объяснение с примерами приведено в этом ответе

понедельник, 6 января 2020 г.

Как определить какая реализация в лямбда-выражении до его исполнения?

#java #лямбда_выражение


Постигаю лямбда-выражения и возник вопрос: «возможно ли определить какая реализация
в момент времени в лямбда-выражении?»
Пример ниже:

interface TestLambda{
    String exec();
}

public class Main {

    public static String meth1(){return "1";}
    public static String meth2(){return "2";}

    public static void main(String[] args) {

        TestLambda l = ((new Random()).nextBoolean())?(Main::meth1):(Main::meth2);
        /* как определить что сейчас в переменой l, т.е. что выполниться при l.exec()  */
        System.out.println(l.exec());

    }
}

    


Ответы

Ответ 1



В комментариях уже написали, что определение реализации интерфейса, тем более лямбда-выражения, противоречит назначению как интерфейсов, так и лямбда выражений. Наверняка задачу для которой потребовался подобный механизм можно решить изменением логики, например: отделить логику определения нужного метода от вызова; вместо лямбда выражений генерировать объекты классов с заданным интерфейсом; оборачивать используемые классы добавляя по мере необходимости идентификацию и вывод в логи. Тем не менее, если будут использоваться только ссылки на методы, то может подойти решение ниже: Если сделать интерфейс сериализуемым: interface TestLambda extends Serializable { String exec(); } , то будет работать такой метод: private static String findMethodName(TestLambda setter) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Class cl = setter.getClass(); Method m = cl.getDeclaredMethod("writeReplace"); m.setAccessible(true); Object replacement = m.invoke(setter); SerializedLambda l = (SerializedLambda) replacement; return l.getImplClass() + "::" + l.getImplMethodName(); } И он будет возвращать названия методов и классов: TestLambda lambda = new Random().nextBoolean() ? Main::meth1 : Main::meth2; System.out.println(findMethodName(lambda)); Работа метода основана на том, что: если целевой тип лямбды сериализуем, то и лямбда сериализуема; для сериализации лямбды создается анонимный тип, в котором определяется метод writeReplace, который возвращает SerializedLambda; используется ссылка на метод и сигнатуры полностью совпадают, вследствие чего getImplClass и getImplMethodName возвращают корректные значения (в любом более сложном случае будут возвращаться имена анонимных классов/методов).

среда, 1 января 2020 г.

В книге Шилдта опечатка или нет?

#java #интерфейс #java_8 #лямбда_выражение


Читаю книгу Шилдта Java 8, главу "Ссылки на статические методы".

Вопрос про класс MyStringOps - видно, что объявлен класс, а не интерфейс, но в книге
почему-то написан интерфейс. Это опечатка? Или чего-то недопонимаю?

// Продемонстрировать ссылку на статический метод
// Функциональный интерфейс для операций с символьными строками

interface StringFunc {
  String func (String n);
}
// в этом интерфейсе определяется статический метод strReverse ()
class MyStringOps {
// Статический метод, изменяющий порядок
// следования символов в строке
  static String strReverse (String str) {
    String result = " " ;
    int i;
    for (i = str.length()- 1; i >= О; i--)
      result += str.charAt (i) ;
    return result;
   }
}
class MethodRefDemo {
// В этом методе функциональный интерфейс указывается в качестве
// типа первого его параметра . Следовательно, ему может быть передан
// любой экземпляр этого интерфейса , включая и ссылку на ме тод
  static StringstringOp (StringFunc sf, String s) { 
  return sf.func(s) ;
  public static void main (String args[])
  {
    String inS tr = "Лямбда- выражения повышают эффективность Java ";
    String out Str;
    // Здесь ссылка на метод strRaverse () передается методу stringOp()
    outStr = stringOp ( MyStringOps::strReverse, inStr);
    System.out.println (" Иcxoднaя строка : "+ inStr);
    System.out.println ("Oбpaщeннaя строка : "+ outStr); 
  }
}

    


Ответы

Ответ 1



Да, действительно, опечатка. В оригинале Java The Complete Reference (Ninth Edition):

воскресенье, 29 декабря 2019 г.

Как присвоить переменной лямбда функцию без использования auto

#cpp #лямбда_выражение


Например есть лямбда которая возвращает какое-либо значение из функции

#include 

int main()
{
    auto f = []()
    {
        std::cout << "Lambda has been invoked" << std::endl;
        return 10;
    };
    std::cout << f() << std::endl;
}


Там указано ключевое слово auto. Но как мне указать определенный тип потому что вот
так не работает

#include 

int main()
{
    int f = []() //поменял auto yfいんt
    {
        std::cout << "Lambda has been invoked" << std::endl;
        return 10;
    };
    std::cout << f() << std::endl;
}


Простите за глупый вопрос
    


Ответы

Ответ 1



#include int main() { int (*f)() = []()->int { std::cout << "Lambda has been invoked" << std::endl; return 10; }; std::cout << f() << std::endl; } Соответственно если вам нужны аргументы: #include int main() { int (*f)(char) = [](char c)->int { std::cout << c << std::endl; return 10; }; std::cout << f('a') << std::endl; }

Ответ 2



Указатели на функции, предложенные @uber42, будут работать только если у лямбды пустой список захвата []. auto a = []{return 42;}; // Компилируется int (*b)() = []{return 42;}; // Компилируется int x = 42; auto с = [x](){return x;}; // Компилируется int (*d)() = [x](){return x;}; // Ошибка: cannot convert 'main()::' to 'int (*)()' in initialization Типы ламбд - это не указатели на функии. Каждая лямбда имеет свой анонимный тип, который по сути является классом с перегруженным оператором (). Лямбду можно преобразовать к указателю на функцию только если у нее пустой список захвата []. Обратите внимание на слова "анонимный тип" - раз у типа нет имени, то его не получится указать явно, и придется использовать auto. Можно использовать std::function (или похожий класс-обертку), который работает всегда, независимо от наличия списка захвата: std::function e = [x](){return x;}; // Компилируется Однако нужно понимать, что std::function занимает больше памяти, чем тип, определенный auto, и вызывается медленнее (или уж точно не быстрее). Поэтому auto предпочтительнее.

воскресенье, 8 декабря 2019 г.

Как происходит передача аргумента лямбда-выражению?

#cpp #lambda #лямбда_выражение


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

Я знаю, что лямбда может принимать аргумент, если после объявления или где-либо после
написать скобки: (arg);.

Также лямбда может захватывать значения перемемнных [=], [x] и т.д.

Однако, как лямбда получает аргумент, имея подобную конструкцию:

[](int n){/*do something*/},

если нигде после её объявления явно не вызываются "скобки со значением", нет доступных
из лямбды внешних ссылок, и она ничего не захватывает?

Подскажие,  как передаются аргументы лямбда-выражениям в подобных случаях?

using namespace std;

int main()
{   
    list numbers;
    numbers.push_back(13);
    numbers.push_back(42);
    numbers.push_back(99);

    const list::const_iterator result = 
        find_if(numbers.begin(), numbers.end(),[](int n) { return (n % 2) == 0; });

    if (result != numbers.end()) {
        cout << "The first even number in the list is " << *result << "." << endl;
    } else {
        cout << "The list contains no even numbers." << endl;
    }
}


Как я понмаю, лямбда-выражение имеет параметр (int n), однако не вижу где лямбда
принимает этот аргумент. И не понимаю как он мог бы передаваться неявно.

...
using namespace std;

int main()
{
    vector srcVec;
    for (int val = 0; val < 10; val++)
    {
        srcVec.push_back(val);
    }

    int result =
        count_if(srcVec.begin(), srcVec.end(), [] (int _n)
        {
            return (_n % 2) == 0;
        });

    cout << result << endl;

    return EXIT_SUCCESS;
}


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

Может быть я не понимаю какого-то банального принципа, который совершенно тривиален,
но был мной упущен? Спасибо.
    


Ответы

Ответ 1



Функция find_if, count_if 3-м аргументом принимают унарный предикат(UnaryPredicat) - функция, которая на вход принимает один аргумент и возвращает тип bool. Требования к предикатной функции. Внутренняя реализация этих функций из стандартной библиотеки вызывает этот унарный предикат, передавая ей в качестве аргумента текущее "тестируемое значение" из коллекции. Унарный предикатом может быть как лямбда-функция, так и любой реализованный самостоятельно функтор.

Ответ 2



Однако, как лямбда получает аргумент, имея подобную конструкцию: [](int n){/*do something*/}, если нигде после её объявления явно не вызываются "скобки со значением"? find_if(numbers.begin(), numbers.end(),[](int n) { return (n % 2) == 0; }); На самом деле здесь все просто. Лямбду вызывает find_if, то есть эти "скобки со значением" находятся где-то внутри find_if. Вот пример реализации find_if с cppreference: template InputIt find_if(InputIt first, InputIt last, UnaryPredicate p) { for (; first != last; ++first) { if (p(*first)) // <--- Вот они скобки (вызов лямбды) { return first; } } return last; }

четверг, 5 декабря 2019 г.

Захват значений в лямбда-выражении

#java #лямбда_выражение



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


Что понимается под захватом значений в лямбда-выражении?
    


Ответы

Ответ 1



Это значит, что в лямбда-выражениях стоит использовать внешние (относительно выражения) неизменяемые значения, а не внешние переменные, значение и внутреннее состояние которых могут меняться. Под внешними неизменяемыми значениями, соответственно, подразумеваются effectively final локальные переменные и поля примитивных типов, а также effectively final объекты, внутреннее состояние которых не будет меняться. Связано это с тем, что Streams и лямбда-выражения проектировались из расчета на их многопоточное использование. Проблема с использованием переменной (counter) вместо значения видна в таком примере: private static class Element { private final int value; public Element(int value) { this.value = value; } public int getValue() { return value; } } private static volatile int counter = 0; public static void main(String[] args) { List list = new ArrayList<>(); for (int i = 0; i < 100 * 1000; i++) { list.add(new Element(1)); } list.parallelStream().forEach(e -> counter += e.getValue()); System.out.println(counter); } Рассчитывать на то, что на экран будет выведено значение 100000, не приходится, потому что налицо race condition. В моём тесте этот код смог получить правильное значение только в 299 случаях из 100 тысяч. Это одна из причин почему локальные переменные, используемые в лямбда-выражении, должны быть effectively final. Допустимость кода int localCounter = 0; list.parallelStream().forEach(e -> localCounter += e.getValue()); Привела бы к race condition для локальной переменной, что стало бы новым витком проблем в многопоточном программировании на Java. Локальные переменные считаются потокобезопасными, и ломать этот принцип разработчикам Java не хотелось. Можно "обдурить" компилятор в плане ограничения на effectively final значение таким образом: int[] localCounter = { 0 }; list.parallelStream().forEach(e -> localCounter[0] += e.getValue()); System.out.println(localCounter[0]); Так что "выстрелить себе в ногу" при использовании effectively final локальной переменной всё же можно. Конечно, не стоит удивляться тому, что значение опять-таки будет посчитано неправильно. На практике так делать определённо не стоит. Да, здесь можно использовать AtomicInteger: AtomicInteger atomicInteger = new AtomicInteger(); list.parallelStream().forEach(e -> atomicInteger.addAndGet(e.getValue())); System.out.println(atomicInteger.get()); Однако это убивает всю идею распараллеливания кода. В данном случае предполагается использование связки из map и reduce: int localCounter = list.parallelStream().map(e -> e.getValue()).reduce(0, (a, b) -> a + b); System.out.println(localCounter); Часть с map и reduce можно записать и так: .map(Element::getValue).reduce(0, Integer::sum) Статью Brian Goetz (автора книги "Java Concurrency in Practice") по этому поводу можно прочитать здесь. Однако проблемы при захвате переменных вместо значений могут возникать не только при параллельном выполнении. Например: private static class Element { public int x; public Element(int x) { this.x = x; } public Function getMapper() { return (e -> e + x); } } public static void main(String[] args) { Element element = new Element(2); List list1 = Arrays.asList(10, 20, 30); Function function1 = element.getMapper(); element.x = 4; List list2 = Arrays.asList(10, 20, 30); Function function2 = element.getMapper(); list1 = list1.stream().map(function1).collect(Collectors.toList()); list2 = list2.stream().map(function2).collect(Collectors.toList()); System.out.println(list1); System.out.println(list2); } В данном коде происходит захват переменной (не effectively final поля) x, из-за чего вместо ожидаемого вывода [12, 22, 32] [14, 24, 34] будет выведено [14, 24, 34] [14, 24, 34] При захвате же значения: public Function getMapper() { int n = x; return (e -> e + n); } такой проблемы/ошибки не возникнет.

Ответ 2



Наверное, автор имел ввиду, что лямбда-выражения должны трогать не переменные, а принимать на вход значение и отдавать значения на выходе. Например: Плохо: final String string = "string"; class.method(() -> string += "abc"); Хорошо: class.method((string) -> string += "abc");

вторник, 26 ноября 2019 г.

Что значит “->” в Java?


Изучаю данный туториал. На некоторых скриншотах есть конструкция ->, например:

vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", res -> {
  if (res.succeeded()) {
    System.out.println("Deployment id is: " + res.result());
  } else {
    System.out.println("Deployment failed!");
  }
});


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


Ответы

Ответ 1



Это лямбда выражение - анонимная функция. Проще говоря, это метод без объявлени (без модификаторов доступа, возвращающие значение и имя). Появились в 8 версии Java. Пример использования Напишем простой пример функционального интерфейса : public interface Lambda { //Метод интерфейса с отсутсвующей реализацией int getDoubleValue(int val); //Метод интерфейса с реализацией по-умолчанию default void printVal(int val) { System.out.println(val); } } Функциональный интерфейс должен иметь только один абстрактный метод. Прочитать причинах такого ограничения можно здесь. Теперь создадим класс для использования public class ClassForLambda { public static void main(String[] args) { //Объявляем ссылку на функциональный интерфейс Lambda lam; //Параметр для нашего абстрактногго метода int num =9; //Прописываем первый вариант реализации lam = (val) -> val * 2; System.out.println(lam.getDoubleValue(num)); //Прописываем второй вариант реализации lam = (val) -> { System.out.println("Your number is "+val); return val * 2; }; System.out.println(lam.getDoubleValue(num)); } } Как можно заметить, обращение к методу не изменилось. Изменению подверглась только реализация. Ссылки: Хабр JavaRush Еще один ответ на StackOverflow

пятница, 14 июня 2019 г.

Имплементация функционального интерфейса

Понимание интерфейса, как конструкции языка, до некоторых пор было мною вроде бы как усвоено. Столкнувшись с лямбда-выражениями это понимание рискует быть пересмотренным. Что я имею ввиду?
Интерфейс позволяет "объединить" в некотором смысле относительно разные классы и работать с ними под определенным углом (интерфейсом). Так или иначе, чтобы им (интерфейсом) воспользоваться, его необходимо имплементировать.
Функциональные интерфейсы, которые определяют один метод, ведут себя иначе? Их не нужно имплементировать, достаточно создать ссылку на них и присвоить этим ссылкам лямбда-выражение?
Мне интересен сам масштаб возможностей, который здесь открывается. Выходит просто ссылка на функциональный интерфейс внутри любого класса способна манипулировать объектами любого класса?


Ответ

Просто как пример, что же такое лямбда в Java
Лямбда
new AccidentsRequest(result -> {if (!result.has("error")) parseJSON(result);}, true);
ТРАХ-ТИБИДОХ, АХАЛАЙ-МАХАЛАЙ!!! И лямбда превращается... В анонимный класс!
new AccidentsRequest(new AsyncTaskCompleteListener() { @Override public void onTaskComplete(JSONObject result) throws JSONException { if (!result.has("error")) Content.this.parseJSON(result); } }, true);
Фактически лямбды в Java - это такой синтаксический сахар для анонимных классов с одним методом.

среда, 12 июня 2019 г.

Сокращенная запись анонимных функций Scala

Изучая Scala нарвался на отличную возможность сократить запись анонимных функций, к примеру: List(2,3,1).sortWith(_ < _) вместо List(2,3,1).sortWith((a,b) => a < b) Не могу найти аналог для одного аргумента, например: List(2,3,1).map(_) //Вызовет ошибку компиляции вместо List(2,3,1).map((a) => a) Понимаю,что это не самая важная штука в Скала, но можно ли как-то записать такую функцию в сокращенном виде?


Ответ

Функция, которая возвращает свой аргумент, называется Тождественное отображение. Такая функция есть в Scala - это identity. Но сокращенной записи для неё нет.
scala> List(1,2,3).map(identity) res1: List[Int] = List(1, 2, 3)

четверг, 16 мая 2019 г.

LINQ из выражения в точечную нотацию

Есть классы моделей
public class Product { public int ProductID { get; set; } public string ProductName { get; set; } public string Category { get; set; } public decimal UnitPrice { get; set; } public int UnitsInStock { get; set; } }
public class Order { public int OrderID { get; set; } public DateTime OrderDate { get; set; } public decimal Total { get; set; } }
Необходимо получить всех клиентов, у которых были заказы, превосходящие по сумме величину value
Я сделал следующий запрос
var customers = from c in dataSource.Customers from o in c.Orders where o.Total > value select c).Distinct();
Выводит то что нужно.
Необходимо выполнить данный запрос в точечной нотации, я пытаюсь сделать так:
var customers = dataSource.Customers.Select(c => c.Orders .Where(o => o.Total > value)) .Distinct();
и получается, что запрос возвращает IEnumerable>, а мне необходим IEnumerable. Скажите пожалуйста в каком месте я ошибаюсь, и как делать запросы подобного вида правильно.


Ответ

Вам нужно написать так:
var customers = dataSource.Customers.Where(c => c.Orders.Any(o => o.Total > value)).Distinct();
Если нужно чтобы хотя бы один заказ больше value, или так:
var customers = dataSource.Customers.Where(c => c.Orders.All(o => o.Total > value)).Distinct();
Если нужно чтобы все заказы больше value
В данном случае не стоит использовать метод Select, так как он возвращает новую коллекцию.

Почему java воспринмает интерфейс comparator как функциональный

Недавно ознакомился с лямбда-выражениями и узнал, что с их помощью можно реализовать Comparator. Но мне не даёт покоя то, что в этом интерфейсе объявлено два не статических и не реализованных по умолчанию метода: compare() и equals(). И несмотря на это, интерфейс считается функциональным, и метод compare() может быть реализован с помощью лябда-выражений.
Как так? Почему метод equals() не воспринимается?


Ответ

Интерфейс Comparator функциональный, потому что equals() - относится к public-методу класcа Object.
Интерфейсы объявляют абстрактные методы относящиеся к public методам Object, но обычно делают это неявным образом и эти методы не учитываются.

воскресенье, 31 марта 2019 г.

Что значит :: знак в java? [дубликат]

На данный вопрос уже ответили: Что значит оператор ::? 1 ответ Часто вижу :: при определении нового экземпляра, в стримах, иногда в лямда выражениях. Что он значит? Что значит List :: stream?
P.S. И как он называется?


Ответ

Оператор "ссылка на метод" (Method Reference). По сути - сокращенная запись для ссылки на метод из функционального интерфейса при использовании лямбды.
List::stream
является ссылкой на метод stream() из интерфейса List
List> list = Arrays.asList( Arrays.asList(1,2,3), Arrays.asList(4,5,6) ); list.steam() .flatMap(List::stream) .forEach(System.out::println);
приведенный отрывок кода означает: 1. Мы инициализируем поток, в котором будет список чисел (Stream>) 2. с помощью flatMap мы объединяем список чисел в поток чисел (Stream) 3. с помощью forEach и ссылки на метод println для каждого элемента из потока вызывается вывод на экран
Если владеете английским, хорошее объяснение с примерами приведено в этом ответе

среда, 9 января 2019 г.

В книге Шилдта опечатка или нет?

Читаю книгу Шилдта Java 8, главу "Ссылки на статические методы".
Вопрос про класс MyStringOps - видно, что объявлен класс, а не интерфейс, но в книге почему-то написан интерфейс. Это опечатка? Или чего-то недопонимаю?
// Продемонстрировать ссылку на статический метод // Функциональный интерфейс для операций с символьными строками
interface StringFunc { String func (String n); } // в этом интерфейсе определяется статический метод strReverse () class MyStringOps { // Статический метод, изменяющий порядок // следования символов в строке static String strReverse (String str) { String result = " " ; int i; for (i = str.length()- 1; i >= О; i--) result += str.charAt (i) ; return result; } } class MethodRefDemo { // В этом методе функциональный интерфейс указывается в качестве // типа первого его параметра . Следовательно, ему может быть передан // любой экземпляр этого интерфейса , включая и ссылку на ме тод static StringstringOp (StringFunc sf, String s) { return sf.func(s) ; public static void main (String args[]) { String inS tr = "Лямбда- выражения повышают эффективность Java "; String out Str; // Здесь ссылка на метод strRaverse () передается методу stringOp() outStr = stringOp ( MyStringOps::strReverse, inStr); System.out.println (" Иcxoднaя строка : "+ inStr); System.out.println ("Oбpaщeннaя строка : "+ outStr); } }


Ответ

Да, действительно, опечатка. В оригинале Java The Complete Reference (Ninth Edition)

среда, 10 октября 2018 г.

Захват значений в лямбда-выражении

Лямбда-выражения должны использоваться для захвата значений, а не переменных. Захват значений побуждает писать код без побочных эффектов, поскольку альтернатива труднее.
Что понимается под захватом значений в лямбда-выражении?


Ответ

Это значит, что в лямбда-выражениях стоит использовать внешние (относительно выражения) неизменяемые значения, а не внешние переменные, значение и внутреннее состояние которых могут меняться. Под внешними неизменяемыми значениями, соответственно, подразумеваются effectively final локальные переменные и поля примитивных типов, а также effectively final объекты, внутреннее состояние которых не будет меняться.
Связано это с тем, что Streams и лямбда-выражения проектировались из расчета на их многопоточное использование.
Проблема с использованием переменной (counter) вместо значения видна в таком примере:
private static class Element { private final int value;
public Element(int value) { this.value = value; }
public int getValue() { return value; } }
private static volatile int counter = 0;
public static void main(String[] args) { List list = new ArrayList<>(); for (int i = 0; i < 100 * 1000; i++) { list.add(new Element(1)); } list.parallelStream().forEach(e -> counter += e.getValue()); System.out.println(counter); }
Рассчитывать на то, что на экран будет выведено значение 100000, не приходится, потому что налицо race condition. В моём тесте этот код смог получить правильное значение только в 299 случаях из 100 тысяч.
Это одна из причин почему локальные переменные, используемые в лямбда-выражении, должны быть effectively final. Допустимость кода
int localCounter = 0; list.parallelStream().forEach(e -> localCounter += e.getValue());
Привела бы к race condition для локальной переменной, что стало бы новым витком проблем в многопоточном программировании на Java. Локальные переменные считаются потокобезопасными, и ломать этот принцип разработчикам Java не хотелось.
Можно "обдурить" компилятор в плане ограничения на effectively final значение таким образом:
int[] localCounter = { 0 }; list.parallelStream().forEach(e -> localCounter[0] += e.getValue()); System.out.println(localCounter[0]);
Так что "выстрелить себе в ногу" при использовании effectively final локальной переменной всё же можно. Конечно, не стоит удивляться тому, что значение опять-таки будет посчитано неправильно. На практике так делать определённо не стоит.
Да, здесь можно использовать AtomicInteger
AtomicInteger atomicInteger = new AtomicInteger(); list.parallelStream().forEach(e -> atomicInteger.addAndGet(e.getValue())); System.out.println(atomicInteger.get());
Однако это убивает всю идею распараллеливания кода.
В данном случае предполагается использование связки из map и reduce
int localCounter = list.parallelStream().map(e -> e.getValue()).reduce(0, (a, b) -> a + b); System.out.println(localCounter);
Часть с map и reduce можно записать и так:
.map(Element::getValue).reduce(0, Integer::sum)
Статью Brian Goetz (автора книги "Java Concurrency in Practice") по этому поводу можно прочитать здесь

Однако проблемы при захвате переменных вместо значений могут возникать не только при параллельном выполнении. Например:
private static class Element { public int x;
public Element(int x) { this.x = x; }
public Function getMapper() { return (e -> e + x); } }
public static void main(String[] args) { Element element = new Element(2); List list1 = Arrays.asList(10, 20, 30); Function function1 = element.getMapper(); element.x = 4; List list2 = Arrays.asList(10, 20, 30); Function function2 = element.getMapper(); list1 = list1.stream().map(function1).collect(Collectors.toList()); list2 = list2.stream().map(function2).collect(Collectors.toList()); System.out.println(list1); System.out.println(list2); }
В данном коде происходит захват переменной (не effectively final поля) x, из-за чего вместо ожидаемого вывода
[12, 22, 32] [14, 24, 34]
будет выведено
[14, 24, 34] [14, 24, 34]
При захвате же значения:
public Function getMapper() { int n = x; return (e -> e + n); }
такой проблемы/ошибки не возникнет.

среда, 3 октября 2018 г.

Что значит “->” в Java?

Изучаю данный туториал. На некоторых скриншотах есть конструкция ->, например:
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", res -> { if (res.succeeded()) { System.out.println("Deployment id is: " + res.result()); } else { System.out.println("Deployment failed!"); } });
Не сталкивался раньше с таким. Подскажите, пожалуйста, что это такое и где можно об этом почитать хорошие материалы.


Ответ

Это лямбда выражение - анонимная функция. Проще говоря, это метод без объявления (без модификаторов доступа, возвращающие значение и имя).
Появились в 8 версии Java.
Пример использования
Напишем простой пример функционального интерфейса :
public interface Lambda { //Метод интерфейса с отсутсвующей реализацией int getDoubleValue(int val);
//Метод интерфейса с реализацией по-умолчанию default void printVal(int val) { System.out.println(val); } }
Функциональный интерфейс должен иметь только один абстрактный метод. Прочитать о причинах такого ограничения можно здесь
Теперь создадим класс для использования
public class ClassForLambda { public static void main(String[] args) { //Объявляем ссылку на функциональный интерфейс Lambda lam; //Параметр для нашего абстрактногго метода int num =9;
//Прописываем первый вариант реализации lam = (val) -> val * 2; System.out.println(lam.getDoubleValue(num));
//Прописываем второй вариант реализации lam = (val) -> { System.out.println("Your number is "+val); return val * 2; }; System.out.println(lam.getDoubleValue(num)); } }
Как можно заметить, обращение к методу не изменилось. Изменению подверглась только реализация.
Ссылки:
Хабр JavaRush Еще один ответ на StackOverflow