Карта - это функция, которая применяет другую функцию ко всем элементам списка, чтобы создать другой список со всеми возвращаемыми значениями в нем. (Еще один способ сказать «применить f к x» - это «вызвать f, передавая его х». Поэтому иногда звучит приятнее сказать «применить» вместо «вызова».)
Вот как карта, вероятно, написана на C # (она называется Select
и находится в стандартной библиотеке):
public static IEnumerable<R> Select<T, R>(this IEnumerable<T> list, Func<T, R> func)
{
foreach (T item in list)
yield return func(item);
}
Поскольку вы - чувак на Java, и Джоэл Спольски любит рассказывать GROSSLY UNFAIR LIES о том, насколько дрянна Java (на самом деле, он не лжет, это дерьмо, но я пытаюсь вас победить) грубая попытка версии Java (у меня нет компилятора Java, и я смутно помню версию Java 1.1!):
// represents a function that takes one arg and returns a result
public interface IFunctor
{
object invoke(object arg);
}
public static object[] map(object[] list, IFunctor func)
{
object[] returnValues = new object[list.length];
for (int n = 0; n < list.length; n++)
returnValues[n] = func.invoke(list[n]);
return returnValues;
}
Я уверен, что это можно улучшить миллионами способов. Но это основная идея.
Reduce - это функция, которая превращает все элементы в списке в одно значение. Для этого ему должна быть предоставлена другая функция func
, которая превращает два элемента в одно значение. Это сработало бы, дав первые два элемента func
. Тогда результат этого вместе с третьим пунктом. Затем результат с четвертым элементом и так далее, пока все элементы не исчезнут, и мы не останемся с одним значением.
В C # сокращение называется Aggregate
и снова в стандартной библиотеке. Я перейду прямо к версии Java:
// represents a function that takes two args and returns a result
public interface IBinaryFunctor
{
object invoke(object arg1, object arg2);
}
public static object reduce(object[] list, IBinaryFunctor func)
{
if (list.length == 0)
return null; // or throw something?
if (list.length == 1)
return list[0]; // just return the only item
object returnValue = func.invoke(list[0], list[1]);
for (int n = 1; n < list.length; n++)
returnValue = func.invoke(returnValue, list[n]);
return returnValue;
}
Эти версии Java нуждаются в добавлении к ним дженериков, но я не знаю, как это сделать в Java. Но вы должны иметь возможность передавать им анонимные внутренние классы для предоставления функторов:
string[] names = getLotsOfNames();
string commaSeparatedNames = (string)reduce(names,
new IBinaryFunctor {
public object invoke(object arg1, object arg2)
{ return ((string)arg1) + ", " + ((string)arg2); }
}
Надеюсь, дженерики избавятся от слепков. Типобезопасный эквивалент в C #:
string commaSeparatedNames = names.Aggregate((a, b) => a + ", " + b);
Почему это "круто"? Простые способы разбить большие вычисления на более мелкие части, чтобы их можно было соединить различными способами, всегда круты. Google применяет эту идею к распараллеливанию, потому что и карты, и сокращения могут использоваться несколькими компьютерами.
Но ключевое требование НЕ в том, чтобы ваш язык мог рассматривать функции как значения. Любой язык ОО может сделать это. Фактическое требование для распараллеливания состоит в том, что маленькие функции func
, которые вы передаете на карту и сокращаете, не должны использовать или обновлять любое состояние. Они должны возвращать значение, которое зависит только от переданных им аргументов. В противном случае результаты будут полностью испорчены, когда вы попытаетесь запустить все это параллельно.