如何使用Lodash流程了解咖喱和功能组成?
import {flow, curry} from 'lodash';
const add = (a, b) => a + b;
const square = n => n * n;
const tap = curry((interceptor, n) => {
interceptor(n);
return n;
});
const trace2 = curry((message, n) => {
return tap((n) => console.log(`${message} is ${n}`), n);
});
const trace = label => {
return tap(x => console.log(`== ${ label }: ${ x }`));
};
const addSquare = flow([add, trace('after add'), square]);
console.log(addSquare(3, 1));
我开始写trace2认为跟踪不起作用,因为“如何可以轻点可能知道关于n或x什么?”。如何使用Lodash流程了解咖喱和功能组成?
但是跟踪确实有效,我不明白它是如何将来自流量的x注入到tap调用中的。任何解释将不胜感激:)
银匙评价
我们只需要开始追查
addSquare(3, 1) // ...
这样的,所谓
= flow([add, trace('after add'), square]) (3, 1)
add(3,1)
4
trace('after add') (4)
tap(x => console.log(`== ${ 'after add' }: ${ x }`)) (4)
curry((interceptor, n) => { interceptor(n); return n; }) (x => console.log(`== ${ 'after add' }: ${ x }`)) (4)
(x => console.log(`== ${ 'after add' }: ${ x }`)) (4); return 4;
console.log(`== ${ 'after add' }: ${ 4 }`); return 4;
~log effect~ "== after add: 4"; return 4
4
square(4)
4 * 4
16
= 16
这样评价你无法看到的基本“技巧”是trace('after add')
返回一个函数,它正在等待最后一个rgument。这是因为trace
是一个双参数函数,它是curried。
无用
我无法表达多么无用和误解flow
功能
function flow(funcs) {
const length = funcs ? funcs.length : 0
let index = length
while (index--) {
if (typeof funcs[index] != 'function') {
throw new TypeError('Expected a function')
}
}
return function(...args) {
let index = 0
let result = length ? funcs[index].apply(this, args) : args[0]
while (++index < length) {
result = funcs[index].call(this, result)
}
return result
}
}
当然,这“作品”因为它描述的工作,但它允许您创建可怕的脆弱代码。
- 环通提供的所有功能,键入检查他们
- 环通提供的所有功能,再次应用它们
- 出于某种原因,允许第一函数(只第一功能)有特殊行为接受1 或更多自变量传入
- 所有非第一个函数最多只能接受1个参数
- 在使用空流的情况下,除了您的第一个输入参数之外的所有内容都将被丢弃
如果您问我,那很奇怪f'合同。你应该问:
- 为什么我们循环两次?
- 为什么第一个函数会得到特殊的异常?
- 我以这种复杂性为代价获得什么?
经典函数组合的两个功能,f
g
和
排版 - 允许数据似乎从瞬间移动状态A
直接状态C
。当然B
状态仍然发生在幕后,但我们可以从认知负载中删除这一事实是一项巨大的礼物。
组成和讨好发挥,从而很好地协同因为
- 功能组合物最适用于一元(单参数)函数
- 咖喱函数接受每个应用程序1个参数
Let's rewri忒代码现在
const add = a => b => a + b
const square = n => n * n;
const comp = f => g => x => f(g(x))
const comp2 = comp (comp) (comp)
const addSquare = comp2 (square) (add)
console.log(addSquare(3)(1)) // 16
“嘿,你骗我!这comp2
是不容易的,在所有的遵循!” –我很抱歉,但是这是因为功能从一开始就注定要失败。为什么寿?
由于成分效果最好的一元函数!我们试图撰写二元函数add
用一元函数square
。
为了更好地说明古典作曲,以及它如何简单的就可以了,让我们来看看使用只是一元函数的序列。
const mult = x => y => x * y
const square = n => n * n;
const tap = f => x => (f(x), x)
const trace = str => tap (x => console.log(`== ${str}: ${x}`))
const flow = ([f,...fs]) => x =>
f === undefined ? x : flow (fs) (f(x))
const tripleSquare = flow([mult(3), trace('triple'), square])
console.log(tripleSquare(2))
// == "triple: 6"
// => 36
哦,顺便说一句,我们重新实现flow
用一个单一的代码行了。
再次
好骗,所以你可能会注意到3
和2
论点在不同的地方通过。你会认为你已经被欺骗了。
const tripleSquare = flow([mult(3), trace('triple'), square])
console.log(tripleSquare(2)) //=> 36
不过的事实是这样的:只要你介绍一个单一的非一元函数到您的函数组合,您不妨重构你的代码。可读性立即下降。如果它会损害可读性,试图保持代码无任何意义是绝对没有意义的。
比方说,我们必须保持这两个参数可用于您的原始addSquare
函数&hellip;那将是什么样子?
const add = x => y => x + y
const square = n => n * n;
const tap = f => x => (f(x), x)
const trace = str => tap (x => console.log(`== ${str}: ${x}`))
const flow = ([f,...fs]) => x =>
f === undefined ? x : flow (fs) (f(x))
const addSquare = (x,y) => flow([add(x), trace('add'), square]) (y)
console.log(addSquare(3,1))
// == "add: 4"
// => 16
OK,所以我们不得不定义addSquare
,因为这
const addSquare = (x,y) => flow([add(x), trace('add'), square]) (y)
这当然不是因为聪明作为lodash版本,但它在如何的明确术语被组合在一起并且几乎具有零复杂性。
事实上,7行代码在这里实现少整个程序比它采取单独落实刚刚lodash flow
功能。
大惊小怪,为什么
一切都在你的程序是一个权衡。我不希望看到初学者与简单的事情斗争。与做这些事情图书馆工作如此复杂是非常令人沮丧的 - 甚至不让我开始Lodash的curry
实现(包括它的疯狂复杂createWrap
)
我的2美分:如果你刚刚开始使用这个东西,图书馆是大锤。他们对自己做出的每一个选择都有自己的理由,但要知道每一个都涉及到权衡。所有这些复杂性都不是完全没有根据的,但它不是你需要作为初学者关心的事情。切断基本功能,并从那里开始工作。
咖喱
因为我提到curry
,这里有3行代码替换几乎任何实际用途Lodash的咖喱。
如果以后这些交易对于更为复杂的执行咖喱,确保你知道你在说什么退出交易 - 否则,你就会承担更多的开销少到无的增益。
// for binary (2-arity) functions
const curry2 = f => x => y => f(x,y)
// for ternary (3-arity) functions
const curry3 = f => x => y => z => f(x,y,z)
// for arbitrary arity
const partial = (f, ...xs) => (...ys) => f(...xs, ...ys)
两种类型的函数组合
还有一件事我应该提到:经典的函数组合应用功能从右到左。因为有些人觉得难读/推理,左到右功能作曲家像flow
和pipe
流行库
左到右的作曲家,
flow
,易于被命名,因为你的眼睛会在意大利面条的形状,您尝试跟踪的数据流因为它移动通过你的节目。(LOL)从右到左作曲家,
composer
,会让你觉得你在第一反向读取,但稍加练习之后,开始觉得很自然。它不会受到意大利面形状数据追踪的困扰。
'tap'是curried,这意味着如果你没有传递足够的参数,它会返回一个仍然需要其余参数的函数。 – 4castle