Groovy: Comparator closure & currying
Рубрика: Development, Groovy | 12 May 2008, 22:41 |
Vadim Voituk
Есть в Groovy такая возможность, как currying (каррирование что-ли?) замыканий.
Само понятие прийшло из функционального программирования и означает преобразование функции с один количеством агрументов, в функцию меньшего количества путем “фиксации” значений некоторых аргументов.
Например есть функция f(x,y,z), тогда её каррированием по фиксированному значению арргумента x=10 будет функция Λ(f)=f(10, y, z).
Термин currying прозшел от имени известного математика Хаскеля Карри, и ещё иногда называется Шейнфинкелизацией (от имени Украинского математика Моисея Шейфинкля, который изобрел это понятие)
Покажу небольшой пример того, как можно использовать каррирование замыканий в Groovy.
Предположим есть обычные POGO класс:
[java]
class User {
String name
String emailAddress
Date dateOfBirth
int weight
String toString() {
name
}
}
[/java]
И стоит задача реализовать сортировку списка обьектов этого класса по каждому из свойств (name, emailAddress, dateOfBirth, weight).
Для выполнения сортировки списка можно использовать метод sort(), в какой необходимо передать свой экземпляр Comparator-а.
Решением задачи “в лоб” будет реализация для каждого из сортируемых свойств отдельного Comparator-а.
Например для свойства name он будет выглядеть приблизительно так:
[java]
public class UserNameComparator implements Comparator<User> {
public int compare(User a, User b) {
def aValue = a.getName();
def bValue = b.getName();
if (!aValue && !bValue)
return 0;
else if (!aValue)
return -1;
else if (!bValue)
return 1;
else
return aValue.compareTo(bValue);
}
}
[/java]
И так 4 раза (с)… Не много ли кода для такой мелочи как сортировка?
Теперь же попробуем реализовать подобный функционал с помощью замыканий Groovy.
Создаем такое вот универсальное замыкание-компаратор (а может функцию 3х аргументов?):
[java]
def comparator = { attribute, a, b ->
def aValue = a.getProperty(attribute)
def bValue = b.getProperty(attribute)
if (!aValue && !bValue)
return 0
else if (!aValue)
return -1
else if (!bValue)
return 1
else
return aValue.compareTo(bValue)
}
[/java]
Теперь для сортировки по каждому свойствую обьекта User используем каррирование замыкания с фиксированием имени свойства:
[java]
usersList.sort( comparator.curry(“name”) )
usersList.sort( comparator.curry(“emailAddress”) )
usersList.sort( comparator.curry(“dateOfBirth”) )
usersList.sort( comparator.curry(“weight”) )
[/java]
Вот так все просто и элегантно – немного функционального программирования, немного meta-программирования, немного duck typing-а.
Tweet
Есть такая штука. Но задачу можно решить еще проще. Метод sort класса List принимает на вход замыкание, результат которого и будет использоваться как “вес” соответствующего элемента:
[java]
def list = []
list << new User (name: “John”, emailAddress : “john@mail.com”, dateOfBirth:new Date(), weight:90)
list << new User (name: “Jane”, emailAddress : “jane@mail.com”, dateOfBirth:new Date(), weight:50)
list << new User (name: “Pete”, emailAddress : “pete@mail.com”, dateOfBirth:new Date(), weight:83)
list.sort {it.name}
println list
list.sort {it.emailAddress}
println list
…
[/java]
Спасибо, наконец то я понял зачем это нужно :)
Маленькая поправка: математика звали Шейнфинкель (без “т”), а шейнфинкелизация — скорее шутка, чем термин.
Роман, спасибо исправил.
C++. Даже не буду убеждать, что он непобедим.
#include “stdafx.h”
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace boost;
using namespace boost::gregorian;
struct User
{
string name;
string email;
date dateOfBirth;
int weight;
User(const string &name, const string &email, const date &dateOfBirth, int weight) : name(name), email(email), dateOfBirth(dateOfBirth), weight(weight) { }
};
int _tmain(int argc, _TCHAR* argv[]) {
vector users;
users.push_back(User(“John”, “john@mail.com”, date(2008, 1, 2), 90));
users.push_back(User(“Jane”, “jane@mail.com”, date(2008, 3, 4), 50));
users.push_back(User(“Pete”, “pete@mail.com”, date(2008, 5, 6), 83));
random_shuffle(users.begin(), users.end());
sort(users.begin(), users.end(), bind(&User::dateOfBirth, _1) > bind(&User::dateOfBirth, _2));
return(0);
}
Покоцались кавычки в инклудах и vector<User> users;
Chabster, спасибо. Крайне интересно!
А как Ваш код использовать для сортировки на моей web-странице?
За 50$/hour я лично расскажу и покажу.
Спасибо за пример про кёриинг, только поставленная в примере задача куда проще и корректней решается имплементацией интерфейса Comparable.