Как хранить данные функциональной цепочки?
Простая функция ниже:
const L = a => L;
формы
L
L(1)
L(1)(2)
...
Похоже, что это формирует список, но фактические данные не хранятся вообще, поэтому, если требуется хранить такие данные, как [1,2]
Какая самая умная практика для выполнения задачи?
const L = (a) => {
// do somthing
return L;
};
Я бы предпочел этот лаконичный стиль функции стрелки и не хотел бы разрушать внешнюю структуру в максимально возможной степени. Конечно, я понимаю, что требуется некоторая модификация внешней структуры, но мне любопытно, что возможно, особенно в функциональном стиле, а не в ОО.
Спецификация просто для хранения данных цепочки функций.
Есть идеи? Благодарю.
Первоначальный простейший подход был бы:
const L = (a) => {
L.val = a;
return L;
};
L.val = L;
может сделать некоторые, но без накопления данных.
{ [Function: L] val: [Circular] }
{ [Function: L] val: 1 }
{ [Function: L] val: 2 }
Обратите внимание:
Каждый список должен быть независимым для накопления.
L(3)(4)
вернусь [3,4]
не [2,3,3,4]
с предварительным накоплением других списков.
Расширенная тема!
4 ответа
Функциональное каррирование и переменные аргументы на самом деле не работают вместе. Это ограничение становится очевидным, когда вы понимаете, что следующие два выражения несовместимы
L (1) -> [ 1 ]
L (1) (2) -> [ 1, 2 ]
Выше L (1)
возвращает список, но во втором выражении мы ожидаем L (1)
быть функцией, к которой мы можем обратиться 2
, L (1)
может быть списком или функцией, которая создает список; это не может быть и то и другое одновременно.
Вот почему другие предложили такие вещи, как .list
чтобы получить реальную стоимость. Вы можете сделать это, но знайте, что использование свойств объекта или использование мутации не обязательно. Вы можете использовать любой сигнал по вашему выбору
const L = (x, acc = []) =>
x === undefined
? acc
: y => L (y, [...acc, x])
console.log
( L () // []
, L (1) () // [ 1 ]
, L (1) (2) () // [ 1, 2 ]
, L (1) (2) (3) () // [ 1, 2, 3 ]
)
Мы можем абстрагироваться от необязательного аргумента, используя вспомогательную вспомогательную функцию. Этот метод похож на решение, которое вы нашли, но здесь мы избегаем неудобного присвоения значений свойствам функций и вместо этого используем простые переменные и не изменяющие действия
const L = init =>
{ const loop = (acc, x) =>
x === undefined
? acc
: y => loop ([...acc, x], y)
return loop ([], init)
}
console.log
( L () // []
, L (1) () // [ 1 ]
, L (1) (2) () // [ 1, 2 ]
, L (1) (2) (3) () // [ 1, 2, 3 ]
)
Или, если ваши требования несколько гибки, проявите творческий подход с более гибким кодированием
const List = x =>
k => k (x)
const append = x => xs =>
List ([ ...xs, x ])
const prepend = x => xs =>
List ([ x, ...xs ])
List ([]) (append (1)) (console.log)
// [ 1 ]
List ([ 2, 3 ]) (append (4)) (append (5)) (prepend (1)) (console.log)
// [ 1, 2, 3, 4, 5 ]
Забавно разрешить допустимые синтаксисы JavaScript до предела, но функции с переменными параметрами лучше всего определять с использованием аргументов с расширением
const L = (...values) =>
values
console.log
( L () // []
, L (1) // [ 1 ]
, L (1, 2) // [ 1, 2 ]
, L (1, 2, 3) // [ 1, 2, 3 ]
)
Менее надуманный пример демонстрирует лучший вариант использования
const max = (x, ...ys) =>
ys.length === 0
? x
: max2 (x, max (...ys))
const max2 = (x, y) =>
x > y ? x : y
console.log
( max (1, 5, 3) // 5
, max (5, 2, 9, 7) // 9
, max (4) // 4
, max () // undefined
)
Вероятно, следующий подход несколько сумасшедший, но он работает:
const L = x => {
const L_ = xs => x => typeof x == 'undefined' ? xs : L_ ([...xs, x])
return L_ ([]) (x)
}
const l1 = L (0) (2) (55) (383) (91) (6) ()
const l2 = L (22) (985) (82) (12) (1034) ()
console.log(l1)
console.log(l2)
В основном, внешний L
получает первый элемент для добавления в список. Последующие звонки будут повторяться внутри L_
объединяющий предыдущий xs
плюс новый x
до тех пор x
лживый обман undefined
Обратите внимание, как внутренний L_
частично применяется, поэтому следующий вызов просто требует нового x
!
Еще один подход: писатель монада
Есть другой возможный подход с использованием монады Writer. Обратите внимание, что этот пример реализации упрощен ради примера:
/////
const Writer = acc => {
return { acc }
}
const of = x => Writer ([x])
const map = f => m => Writer ([...m.acc, f (m.acc)])
const read = m => m.acc
/////
const pipe = xs => x => xs.reduce ((o, f) => f (o), x)
const K = x => () => x
const list = pipe ([
of,
map (K (1)),
map (K (38)),
map (K (1781)),
read
]) (123)
const list2 = pipe ([
of,
map (x => x * 2),
map (x => x + 4),
map (x => x * 10),
read
]) (123)
console.log ('#1 list:')
console.log (list)
console.log ('#2 list:')
console.log (list2)
* Это не должно быть ложным, потому что 0
Evals как false
так L
не поддержит это как возможную ценность.
Благодаря доброте и мастерству @Adelin, я нашел идеальный подход.
const L = (a) => {
const m = a => (m.list ? m.list : m.list = [])
.push(a) && m; //use `concat` and refactor needed instead of `push` that is not immutable
return m(a); // Object construction!
};
console.log(L);
console.log(L(2));
console.log(L(1)(2)(3))
некоторый вывод:
{ [Function: m] list: [ 2 ] }
{ [Function: m] list: [ 1, 2, 3 ] }
Как это элегантно.
Опять же, кредит в 95% вклада идет на @Adelin. Моя оценка
Вот самое близкое, чего я могу достичь.
const L = a => b => {
if (!Array.isArray(a)) a = [a];
a.push(b);
return a;
}
x = L(L(L(3)(5))(5))(3)
console.log(x); // [3, 5, 5, 3]
Я надеюсь, что вы любите скобки!