是否有任何一个 (R.either) 的可变参数版本?
Is there a Variadic Version of either (R.either)?
我需要 R.either
的可变版本。在网上做了一些搜索后,我还没有找到解决方案。 R.anyPass
可以,但它 returns 是一个布尔值而不是原始值。是否已经有我忽略的解决方案?如果不是,编写可变参数效用函数的最佳方法是什么?
一个例子:
const test = variadicEither(R.multiply(0), R.add(-1), R.add(1), R.add(2))
test(1) // => 2
您可以组合使用 reduce
+ reduced
:
const z = (...fns) => x => reduce((res, fn) => res ? reduced(res) : fn(x), false, fns);
console.log(
z(always(0), always(10), always(2))(11),
z(always(0), always(''), always(15), always(2))(11),
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {reduce, reduced, always} = R;</script>
(上次尝试)
我会这样做:
const z = unapply(curry((fns, x) => find(applyTo(x), fns)(x)));
console.log(
z(always(0), always(15), always(2))(10),
z(always(0), always(''), always(NaN), always(30), always(2))(10),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {unapply, curry, find, applyTo, always} = R;</script>
虽然这有三个主要警告!
- 您必须在两次 "passes" 中调用
z
,即 z(...functions)(x)
- 虽然应该很容易添加,但是我没在意没有函数的情况"matches"
- 也许没什么大不了但值得注意:匹配的谓词将被执行两次
第一个函数 findTruthyFn
用于查找真实函数,或者如果其中 none 个函数 return 是真实结果,则采用最后一个函数。
第二个函数fn
获取函数列表和值,使用findTruthyFn
查找函数,并将其应用于值以获得结果。
const { either, pipe, applyTo, flip, find, always, last, converge, call, identity } = R
const findTruthyFn = fns => either(
pipe(applyTo, flip(find)(fns)),
always(last(fns))
)
const fn = fns => converge(call, [findTruthyFn(fns), identity])
const check = fn([x => x + 1, x => x - 1])
console.log(check(1))
console.log(check(-1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
如果你想将匹配函数的调用次数限制为一次,你可以在测试之前记住这些函数:
const { either, pipe, applyTo, flip, find, always, last, converge, call, identity, map, memoizeWith } = R
const findTruthyFn = fns => either(
pipe(applyTo, flip(find)(map(memoizeWith(identity), fns))),
always(last(fns))
)
const fn = fns => converge(call, [findTruthyFn(fns), identity])
const check = fn([
x => console.log('1st called') || x + 1,
x => console.log('2nd called') || x - 1
])
console.log(check(1))
console.log(check(-1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
这个 (non-Ramda) 版本非常简单,它似乎满足了需要:
const varEither = (...fns) => (x, res = null, fn = fns.find(fn => res = fn(x))) => res
如果您需要为生成的函数提供多个参数,也不会更难:
const varEither = (...fns) => (...xs) => {
let res = null;
fns .find (fn => res = fn (...xs) )
return res;
}
但我不得不说调用 fns.find
因为它的 side-effects 确实看起来很脏,这可能会让我选择 customcommander 的更新版本而不是这个。
更新:
我个人更喜欢以前的版本,它更干净,但你最终可以进一步 ramdify(由于递归,你不能完全写 point-free):
const either = (...fns) => R.converge(R.either, [
R.head,
R.pipe(
R.tail,
R.ifElse(R.isEmpty, R.identity, R.apply(either)),
),
])(fns);
const result = either(R.always(null), R.always(0), R.always('truthy'))();
console.log(`result is "${result}"`);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
更新:
根据@customcommander 的建议,递归可以嵌套在正确的分支中以获得更简洁的脚本...
const either = (...fns) => (...values) => {
const [left = R.identity, ...rest] = fns;
return R.either(
left,
rest.length ? either(...rest) : R.identity,
)(...values);
}
const result = either(R.always(null), R.always(0), R.always('truthy'))();
console.log(`result is "${result}"`);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
您可以递归调用 R.either
...
const either = (...fns) => (...values) => {
const [left = R.identity, right = R.identity, ...rest] = fns;
return R.either(left, right)(...values) || (
rest.length ? either(...rest)(...values) : null
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
没有 Ramda ...
我可能会使用简单的递归来写这个 -
const always = x =>
_ => x
const identity = x =>
x
const veither = (f = identity, ...more) => (...args) =>
more.length === 0
? f (...args)
: f (...args) || veither (...more) (...args)
const test =
veither
( always (0)
, always (false)
, always ("")
, always (1)
, always (true)
)
console .log (test ())
// 1
但还有更多...
R.either
必须是 Ramda 库中比较古怪的函数之一。如果您仔细阅读 documentation,R.either
有两 (2) 个行为变体:它可以 return -
一个函数,它将其参数传递给两个函数中的每一个,f
和 g
,并且 return 是第一个 truthy 值 - 如果 f
的结果为真,g
将 不会 被评估。
或者,应用函子
R.either
的签名是 -
either : (*… → Boolean) → (*… → Boolean) → (*… → Boolean)
但这肯定是在捏造一点。对于我们上面的两个案例,以下两个签名更接近 -
// variant 1
either : (*… → a) → (*… → b) → (*… → a|b)
// variant 2
either : Apply f => f a → f b → f (a|b)
让我们通过简单的测试来确认这两个变体 -
const { always, either } =
R
const { Just, Nothing } =
folktale.maybe
// variant 1 returns a function
const test =
either
( always (0)
, always (true)
)
console.log(test()) // => true
// variant 2 returns an applicative functor
const result =
either
( Just (false)
, Just (1)
)
console.log(result) // => Just { 1 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>
加倍...
现在让我们制作一个 super-powered veither
来提供与 R.either
-
相同的双重能力
const vor = (a, ...more) =>
a || vor (...more)
const veither = (f, ...more) =>
f instanceof Function
// variant 1
? (...args) =>
f (...args) || veither (...more) (...args)
// variant 2
: liftN (more.length + 1, vor) (f, ...more)
它的工作方式与 R.either
相同,只是现在它接受两个 或更多 个参数。每个变体的行为都得到支持 -
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
test () // => "fn"
// variant 2 returns an applicative functor
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
// => Just { "ap" }
你可以view the source换取R.either
并与上面的veither
进行比较。 Uncurried 和 restyled,你可以在这里看到它的许多相似之处 -
// either : (*… → a) → (*… → b) → (*… → a|b)
// either : Apply f => f a -> f b -> f (a|b)
const either = (f, g) =>
isFunction (f)
// variant 1
? (...args) =>
f (...args) || g (...args)
// variant 2
: lift (or) (f, g)
展开下面的代码片段以在您自己的浏览器中验证结果 -
const { always, either, liftN } =
R
const { Just, Nothing } =
folktale.maybe
const vor = (a, ...more) =>
a || vor (...more)
const veither = (f, ...more) =>
f instanceof Function
? (...args) =>
f (...args) || veither (...more) (...args)
: liftN (more.length + 1, vor) (f, ...more)
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
console .log (test ()) // "fn"
// variant 2 returns an applicative functor
const result =
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
console .log (result) // Just { "ap" }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>
事后和远见...
只需一个小技巧,我们就可以跳过所有推理自己的仪式veither
。在此实现中,我们只需重复调用 R.either
-
const veither = (f, ...more) =>
more.length === 0
? R.either (f, f) // ^_^
: R.either (f, veither (...more))
我向您展示这个是因为它工作得很好并且保留了两个变体的行为,但应该避免它,因为它构建了一个更复杂的计算树。尽管如此,请展开下面的代码片段以验证它是否有效 -
const { always, either } =
R
const { Just, Nothing } =
folktale.maybe
const veither = (f, ...more) =>
more.length === 0
? either (f, f)
: either (f, veither (...more))
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
console .log (test ()) // "fn"
// variant 2 returns an applicative functor
const result =
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
console .log (result) // Just { "ap" }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>
我需要 R.either
的可变版本。在网上做了一些搜索后,我还没有找到解决方案。 R.anyPass
可以,但它 returns 是一个布尔值而不是原始值。是否已经有我忽略的解决方案?如果不是,编写可变参数效用函数的最佳方法是什么?
一个例子:
const test = variadicEither(R.multiply(0), R.add(-1), R.add(1), R.add(2))
test(1) // => 2
您可以组合使用 reduce
+ reduced
:
const z = (...fns) => x => reduce((res, fn) => res ? reduced(res) : fn(x), false, fns);
console.log(
z(always(0), always(10), always(2))(11),
z(always(0), always(''), always(15), always(2))(11),
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {reduce, reduced, always} = R;</script>
(上次尝试)
我会这样做:
const z = unapply(curry((fns, x) => find(applyTo(x), fns)(x)));
console.log(
z(always(0), always(15), always(2))(10),
z(always(0), always(''), always(NaN), always(30), always(2))(10),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {unapply, curry, find, applyTo, always} = R;</script>
虽然这有三个主要警告!
- 您必须在两次 "passes" 中调用
z
,即z(...functions)(x)
- 虽然应该很容易添加,但是我没在意没有函数的情况"matches"
- 也许没什么大不了但值得注意:匹配的谓词将被执行两次
第一个函数 findTruthyFn
用于查找真实函数,或者如果其中 none 个函数 return 是真实结果,则采用最后一个函数。
第二个函数fn
获取函数列表和值,使用findTruthyFn
查找函数,并将其应用于值以获得结果。
const { either, pipe, applyTo, flip, find, always, last, converge, call, identity } = R
const findTruthyFn = fns => either(
pipe(applyTo, flip(find)(fns)),
always(last(fns))
)
const fn = fns => converge(call, [findTruthyFn(fns), identity])
const check = fn([x => x + 1, x => x - 1])
console.log(check(1))
console.log(check(-1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
如果你想将匹配函数的调用次数限制为一次,你可以在测试之前记住这些函数:
const { either, pipe, applyTo, flip, find, always, last, converge, call, identity, map, memoizeWith } = R
const findTruthyFn = fns => either(
pipe(applyTo, flip(find)(map(memoizeWith(identity), fns))),
always(last(fns))
)
const fn = fns => converge(call, [findTruthyFn(fns), identity])
const check = fn([
x => console.log('1st called') || x + 1,
x => console.log('2nd called') || x - 1
])
console.log(check(1))
console.log(check(-1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
这个 (non-Ramda) 版本非常简单,它似乎满足了需要:
const varEither = (...fns) => (x, res = null, fn = fns.find(fn => res = fn(x))) => res
如果您需要为生成的函数提供多个参数,也不会更难:
const varEither = (...fns) => (...xs) => {
let res = null;
fns .find (fn => res = fn (...xs) )
return res;
}
但我不得不说调用 fns.find
因为它的 side-effects 确实看起来很脏,这可能会让我选择 customcommander 的更新版本而不是这个。
更新:
我个人更喜欢以前的版本,它更干净,但你最终可以进一步 ramdify(由于递归,你不能完全写 point-free):
const either = (...fns) => R.converge(R.either, [
R.head,
R.pipe(
R.tail,
R.ifElse(R.isEmpty, R.identity, R.apply(either)),
),
])(fns);
const result = either(R.always(null), R.always(0), R.always('truthy'))();
console.log(`result is "${result}"`);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
更新:
根据@customcommander 的建议,递归可以嵌套在正确的分支中以获得更简洁的脚本...
const either = (...fns) => (...values) => {
const [left = R.identity, ...rest] = fns;
return R.either(
left,
rest.length ? either(...rest) : R.identity,
)(...values);
}
const result = either(R.always(null), R.always(0), R.always('truthy'))();
console.log(`result is "${result}"`);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
您可以递归调用 R.either
...
const either = (...fns) => (...values) => {
const [left = R.identity, right = R.identity, ...rest] = fns;
return R.either(left, right)(...values) || (
rest.length ? either(...rest)(...values) : null
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
没有 Ramda ...
我可能会使用简单的递归来写这个 -
const always = x =>
_ => x
const identity = x =>
x
const veither = (f = identity, ...more) => (...args) =>
more.length === 0
? f (...args)
: f (...args) || veither (...more) (...args)
const test =
veither
( always (0)
, always (false)
, always ("")
, always (1)
, always (true)
)
console .log (test ())
// 1
但还有更多...
R.either
必须是 Ramda 库中比较古怪的函数之一。如果您仔细阅读 documentation,R.either
有两 (2) 个行为变体:它可以 return -
一个函数,它将其参数传递给两个函数中的每一个,
f
和g
,并且 return 是第一个 truthy 值 - 如果f
的结果为真,g
将 不会 被评估。或者,应用函子
R.either
的签名是 -
either : (*… → Boolean) → (*… → Boolean) → (*… → Boolean)
但这肯定是在捏造一点。对于我们上面的两个案例,以下两个签名更接近 -
// variant 1
either : (*… → a) → (*… → b) → (*… → a|b)
// variant 2
either : Apply f => f a → f b → f (a|b)
让我们通过简单的测试来确认这两个变体 -
const { always, either } =
R
const { Just, Nothing } =
folktale.maybe
// variant 1 returns a function
const test =
either
( always (0)
, always (true)
)
console.log(test()) // => true
// variant 2 returns an applicative functor
const result =
either
( Just (false)
, Just (1)
)
console.log(result) // => Just { 1 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>
加倍...
现在让我们制作一个 super-powered veither
来提供与 R.either
-
const vor = (a, ...more) =>
a || vor (...more)
const veither = (f, ...more) =>
f instanceof Function
// variant 1
? (...args) =>
f (...args) || veither (...more) (...args)
// variant 2
: liftN (more.length + 1, vor) (f, ...more)
它的工作方式与 R.either
相同,只是现在它接受两个 或更多 个参数。每个变体的行为都得到支持 -
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
test () // => "fn"
// variant 2 returns an applicative functor
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
// => Just { "ap" }
你可以view the source换取R.either
并与上面的veither
进行比较。 Uncurried 和 restyled,你可以在这里看到它的许多相似之处 -
// either : (*… → a) → (*… → b) → (*… → a|b)
// either : Apply f => f a -> f b -> f (a|b)
const either = (f, g) =>
isFunction (f)
// variant 1
? (...args) =>
f (...args) || g (...args)
// variant 2
: lift (or) (f, g)
展开下面的代码片段以在您自己的浏览器中验证结果 -
const { always, either, liftN } =
R
const { Just, Nothing } =
folktale.maybe
const vor = (a, ...more) =>
a || vor (...more)
const veither = (f, ...more) =>
f instanceof Function
? (...args) =>
f (...args) || veither (...more) (...args)
: liftN (more.length + 1, vor) (f, ...more)
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
console .log (test ()) // "fn"
// variant 2 returns an applicative functor
const result =
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
console .log (result) // Just { "ap" }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>
事后和远见...
只需一个小技巧,我们就可以跳过所有推理自己的仪式veither
。在此实现中,我们只需重复调用 R.either
-
const veither = (f, ...more) =>
more.length === 0
? R.either (f, f) // ^_^
: R.either (f, veither (...more))
我向您展示这个是因为它工作得很好并且保留了两个变体的行为,但应该避免它,因为它构建了一个更复杂的计算树。尽管如此,请展开下面的代码片段以验证它是否有效 -
const { always, either } =
R
const { Just, Nothing } =
folktale.maybe
const veither = (f, ...more) =>
more.length === 0
? either (f, f)
: either (f, veither (...more))
// variant 1 returns a function
const test =
veither
( always (false)
, always (0)
, always ("fn")
, always (2)
)
console .log (test ()) // "fn"
// variant 2 returns an applicative functor
const result =
veither
( Just (0)
, Just (false)
, Just ("")
, Just ("ap")
, Just (2)
)
console .log (result) // Just { "ap" }
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/folktale/2.0.1/folktale.min.js"></script>