В поисках аналога функций первого порядка в СУБД Caché
Пост написан в дополнение к статье Декларативная разработка на Caché.
[2, 3, 5, 7, 11, 13, 17].forEach(function(i) {
console.log(i);
});
Как делать такое в Caché с помощью COS?
Под катом несколько упражнений на заданную тему.
Чтобы увидеть, как средствами COS — одного из серверных языков, встроенных в Caché — можно добиться такого же лаконичного, наглядного и гибкого кода, был разработан собственный класс test.ForEach для работы с коллекциями-списками.
/// Класс-итератор для коллекций.
Class test.ForEach Extends %RegisteredObject [ Final ]
{
/// Коллекция-список почти любого типа.
Property collection As %Collection.AbstractList [ Internal, Private, ReadOnly, Transient ];
/// Инициализация свойства
///
Допустимые аргументы val:
///
///
///
Method %OnNew (val, sep = »,») As %Status [ Private, ServerOnly = 1 ]
{
if $IsObject(val) {
quit:'val.%Extends(»%Collection.AbstractList») $$$ERROR($$$OrefInvalid, val)
set i%collection=val
}else{
set i%collection=##class(%ListOfDataTypes).%New()
do …collection.InsertList($select($listvalid(val): val,1: $listfromstring(val, sep)))
}
quit $$$OK
}
/// Основной метод-обработчик.
///
///
Аргументы:
///
///
func:
/// Примеры вызова:
/// s obj=##class (test.ForEach).%New (»2,3,5»)
/// ; для каждого элемента коллекции будет вызван соответствующий метод класса с передачей аргументов.
/// ; Первый аргумент выходной/входной, остальные — входные, но это лишь способ соглашения.
/// ; При желании можно поменять их местами, сделать несколько выходных и т.д.
/// d obj.Do («className: methodName»,.result, param1, param2, paramN)
/// ; сумма элементов (имеет смысл лишь для коллекции чисел)
/// d obj.Do (»+»,.result)
/// ; произведение (имеет смысл лишь для коллекции чисел)
/// d obj.Do (»*»,.result)
/// ; конкатенация с разделителем (имеет смысл лишь для коллекции простых типов)
/// d obj.Do (»_»,.result, separator)
/// ; минимум (имеет смысл лишь для коллекции простых типов)
/// d obj.Do («min»,.result)
/// ; максимум (имеет смысл лишь для коллекции простых типов)
/// d obj.Do («max»,.result)
/// ; среднее (имеет смысл лишь для коллекции чисел)
/// d obj.Do («avg»,.result)
/// ; любой код, где el=элемент коллекции, args=переданные аргументы
/// d obj.Do ($lb («s args (1,1)=args (1,1)+el»),.result) ; эквивалент »+»
/// ; вызов подпрограммы sub^prog с передачей аргументов
/// d obj.Do ($lb («d sub^prog (el, args…)»),.result, param1, param2, paramN)
///
///
Method Do (func = »+», Args…) As %Status
{
#define ReturnOnError (%expr) s sc=%expr ret:$$$ISERR(sc) sc
quit:'…collection.Count() $$$OK
if func=»+» {
set func=$listbuild(«s args (1,1)=args (1,1)+el»)
}elseif func=»*» {
set func=$listbuild(«s args (1,1)=args (1,1)*el»)
}elseif func=»_» {
set func=$listbuild(«s args (1,1)=args (1,1)_args (1,2)_el»)
}elseif func=«min» {
set func=$listbuild(«s: el
}elseif func=«max» {
set func=$listbuild(«s: el>args (1,1) args (1,1)=el»), Args(1)=-999999999999999
}elseif func=«avg» {
set func=$listbuild(«s args (1,1)=el/args (1,2)+args (1,1)»), Args=2, Args(2)=…collection.Count() kill Args(1)
}
if $listvalid(func) {
set cmd=$list(func)
$$$ReturnOnError(##class(%Routine).CheckSyntax(» »_cmd))
set cmd=»(el, args…){»_cmd_»}»
set key = »
for {
set el = …collection.GetNext(.key)