Примечание редактора. Это старая запись от марта 2013 года из старого блога. По-видимому, он все еще получает достаточно просмотров, чтобы нуждаться в обновлении, поскольку ссылки изменились. Повторная публикация здесь с исправлениями URL.
Существует область, в которой карри рассматривается как один из основных элементов региональной кухни. Это не тот домен; хотя в этой области я бы сказал, что нам нужна паприка.
Если термины карри или частичное применение вам совсем незнакомы, я настоятельно рекомендую последний опус на эту тему Рега Брейтуэйта. Поддержку оказывают +Бен Алман с его собственным потрошащим проявлением силы, а также +Аксель Раушмайер с его очень, чрезвычайно, обычно авторитетной записью. Если вы когда-либо следили за мной здесь раньше, вы, возможно, помните туман, окружающий идею, касающуюся частичного применения из Хороших чтений, где я ссылался на (опять же на мистера Брейтуэйта) тощих.
Не смущайтесь, не расстраивайтесь, не унывайте и не унывайте. Каррирование и частичное применение не так-то просто получить. Литература по этому вопросу, хотя и обширна, плотна. Даже в самых умелых руках попытки принести эти фолианты с горы Синай обычно не приводили к большей ясности.
Но это еще не очередная индоктринация по этому вопросу. Для этого обратитесь к вышестоящим властям. Здесь я пытаюсь использовать потенциал во благо. Используя оба ингредиента, можно создать семантику для запросов к объектам JavaScript с синтаксисом, напоминающим SQL. Есть ли другие решения для этого? "Да".
После того, как вы разобрались с понятиями, должно быть несложно увидеть реализацию нескольких стандартных методов частичного приложения: карта, фильтр и свернуть. Множество библиотек уже постарались и написали эти для нас, но ради того, чтобы было что писать, давайте реализуем их снова (В реальном мире вам лучше взять какой-нибудь функциональный js или wu.js уже построили)!
function curryLeft(func) { var slice = Array.prototype.slice; var args = slice.call(arguments, 1); return function() { return func.apply(this, args.concat(slice.call(arguments, 0))); } } function foldLeft(func,newArray,oldArray) { var accumulation = newArray; each(oldArray, function(val) { accumulation = func(accumulation, val); }); return accumulation; } function map(func, array) { var onIteration = function(accumulation, val) { return accumulation.concat(func(val)); }; return foldLeft(onIteration, [], array) } function filter(func, array) { var onIteration = function(accumulation, val) { if(func(val)) { return accumulation.concat(val); } else { return accumulation; } }; return foldLeft(onIteration, [], array) }
Только с ними мы можем сделать что-то почти крутое. Мы можем расширить родной класс Array, добавив несколько новых методов:
Object.defineProperties(Array.prototype, { '_where': { value: function(func) { return filter(func, this); } }, '_select': { value: function(func) { return map(func, this); } } });
На данный момент, учитывая экземпляр массива (спасибо Faker), например:
var somePeople = [ {"FirstName":"Cristina", "LastName":"Quigley", "PhoneNumber":"1-189-868-2830", "Email":"[email protected]", "Id":0}, {"FirstName":"Eriberto", "LastName":"Bailey", "PhoneNumber":"1-749-549-2050 x36612", "Email":"[email protected]", "Id":1}, {"FirstName":"Amina", "LastName":"Schaden", "PhoneNumber":"463-301-9579 x9511", "Email":"[email protected]", "Id":2}];
Если бы мы хотели выбрать FirstName каждой записи, мы могли бы сделать что-то грубое, например:
somePeople._select(function(row) { return row.FirstName });
Но это все равно слишком тупо. С небольшим количеством карри мы можем сделать его лучше. Мы использовали частичное приложение, чтобы добраться до «_select», но мы можем переключиться на карри, чтобы получить лучший механизм запросов. Во-первых, давайте определим метод запроса:Обновление: технически здесь нам не нужно карри, поскольку мы не абстрагируем объект «запрос» как параметр. Спасибо Томасу Бюретту в комментариях.
var query = function(array) { var tables = []; tables.push(array); var _query = { tables: tables, from: from, select: select, run: run }; return _query; };
методы select и from просты:
function select() { var query = this; var slice = Array.prototype.slice; var args = slice.call(arguments, 0); query.columns = query.columns || []; each(args, function(argumentValue) { query.columns.push(argumentValue); }); return query; } function from(array) { var query = this; query.tables.push(array); return query; }
который затем оставляет только выполнение. Я намеренно не оптимизировал этот метод для иллюстрации: в отсутствие инструментов такой код выглядит так. Посмотрите на избыточность и дублирование. Поразитесь неэлегантности. Цените тот факт, что это работает.
function run() { var query = this; var ret = []; if (query.columns.length > 0) { var results = []; each(query.columns, function(columnName) { each(query.tables, function(tbl) { if (Array.isArray(tbl)) { var res = {}; var val = tbl._select(function(val) { return val[columnName]; }); if (val) { res[columnName] = val; results.push(res); } } }, true); }); var returnRows = []; if(results && results.length > 0) { var firstResult = results[0]; each(firstResult, function(val, key) { each(val, function(cell){ var row = {}; row[key] = cell; each(results.slice(1), function(result) { each(result, function(v,k){ each(v, function(c) { row[k] = c; }) },true) },true) returnRows.push(row); },true); },true) } } return returnRows; }
Теперь это дает синтаксис, который больше похож на SQL:
var newQuery = query(people).select('FirstName', 'LastName'); var results = newQuery.run();
Отсюда «где», «присоединиться» (да, я сказал «СОЕДИНИТЬСЯ»), «orderby» и «groupby» — все это детали реализации. Это всего лишь пост с доказательством концепции, но, учитывая некоторые большие наборы данных Faker, он уже работает довольно хорошо, учитывая его ограничения. Рефакторинг «перехода» к методу, использующему частичное применение, даст горы.
Как всегда, все, что я пишу в блоге и коде, является общественным достоянием. Вы можете просмотреть исходный код из проекта oj-sql здесь, поработать со мной над c9 здесь или сделать все, что вам взбредет в голову. Пусть ветер, поражающий твою прихоть, всегда будет за твоей спиной.