如何改进我编写的 ramda 代码?
How can I improve the ramda code I have written?
我是 ramda.js 的新手,我编写了以下代码来检查该值是否尚未以提供的列表中的后缀结尾,它添加了 "px" 后缀
function addSuffix(value, suffix = 'px', test = ['px', 'pt', '%']) {
return compose(
concat(value),
ifElse(anyPass(map(endsWith, test)), () => '', () => suffix)
)(value);
}
也许我要提出的第一个建议是避免使用默认参数。他们通常不能很好地使用柯里化函数。
您可以编写一个 return 函数来检查字符串是否以给定的后缀列表结尾:
const hasSuffixFn = compose(anyPass, map(endsWith));
const hasSuffix = hasSuffixFn(['px', 'pt', '%']);
hasSuffix('foo'); // false
hasSuffix('1px'); // true
然后你可以有一个函数,它接受一个后缀列表和一个后缀,returns 一个函数,如果它不存在,它将附加该后缀:
const addSuffix = (suffixes, suffix) => unless(hasSuffix(suffixes), flip(concat)(suffix));
const addPx = addSuffix(['px', 'pt', '%'], 'px');
addPx('10'); // '10px'
addPx('10px'); // '10px'
addPx('10pt'); // '10pt'
请注意,您可以将 addSuffix
重写为 pointfree 样式 useWith
:
const addSuffix = useWith(unless, [hasSuffix, flip(concat)]);
总而言之
const hasSuffix = compose(anyPass, map(endsWith));
const addSuffix = (suffixes, suffix) => unless(hasSuffix(suffixes), flip(concat)(suffix));
const addPx = addSuffix(['px', 'pt', '%'], 'px');
console.log(addPx('10'));
console.log(addPx('10px'));
console.log(addPx('10pt'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {anyPass, endsWith, unless, flip, concat, compose, map} = R;</script>
附录
为什么不使用默认参数?
我不知道在函数式编程中使用默认参数是否是一种推荐做法,但我意识到当您需要部分应用函数时,它们通常会妨碍您。
看看这个:
这里我们有一个将三个数相加的函数:
const foo = (a, b, c) => a + b + c;
foo(10, 20, 30); // 60
我们可以柯里化它并开始部分应用该函数:
const foo_curried = curry(foo);
foo_curried(10, 20, 30); // 60
foo_curried(10, 20)(30); // 60
foo_curried(10)(20, 30); // 60
foo_curried(10)(20)(30); // 60
让我们编写一个类似的函数,但使用默认参数。这按预期工作:
const bar = (a=10, b=20, c=30) => a + b + c;
bar(); // 60
bar(110); // 160
bar(110, 220); // 360
bar(110, 220, 330); // 660
但是,如果您想柯里化并部分应用它,则会出现错误:
const bar_curried = curry(bar);
bar_curried(); // 60
bar_curried(110); // 160
bar_curried(110)(220, 330); // Error!
为什么?柯里化函数会一直等到您提供了它的所有参数。在那之前它保持 returning 一个接受剩余参数的函数。但是,如果您部分应用具有默认参数的函数,那么您无法真正预测柯里化函数的结果 return:最终结果还是接受剩余参数的函数?
在最后一个示例中,bar_curried(110)
return 直接返回结果,即 110 + 20 + 30
,当您尝试将数字作为函数调用时会出现错误。
您也可以避免传递后缀列表,因为它看起来是多余的...只需确保给定的 value
不以数字结尾:
const ensureSuffix = R.curry((suffix, value) =>
R.unless(R.test(/\D$/), v => `${v}${suffix}`, value),
);
const addPx = ensureSuffix('px');
console.log(addPx(15));
console.log(addPx('15pt'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
我是 ramda.js 的新手,我编写了以下代码来检查该值是否尚未以提供的列表中的后缀结尾,它添加了 "px" 后缀
function addSuffix(value, suffix = 'px', test = ['px', 'pt', '%']) {
return compose(
concat(value),
ifElse(anyPass(map(endsWith, test)), () => '', () => suffix)
)(value);
}
也许我要提出的第一个建议是避免使用默认参数。他们通常不能很好地使用柯里化函数。
您可以编写一个 return 函数来检查字符串是否以给定的后缀列表结尾:
const hasSuffixFn = compose(anyPass, map(endsWith));
const hasSuffix = hasSuffixFn(['px', 'pt', '%']);
hasSuffix('foo'); // false
hasSuffix('1px'); // true
然后你可以有一个函数,它接受一个后缀列表和一个后缀,returns 一个函数,如果它不存在,它将附加该后缀:
const addSuffix = (suffixes, suffix) => unless(hasSuffix(suffixes), flip(concat)(suffix));
const addPx = addSuffix(['px', 'pt', '%'], 'px');
addPx('10'); // '10px'
addPx('10px'); // '10px'
addPx('10pt'); // '10pt'
请注意,您可以将 addSuffix
重写为 pointfree 样式 useWith
:
const addSuffix = useWith(unless, [hasSuffix, flip(concat)]);
总而言之
const hasSuffix = compose(anyPass, map(endsWith));
const addSuffix = (suffixes, suffix) => unless(hasSuffix(suffixes), flip(concat)(suffix));
const addPx = addSuffix(['px', 'pt', '%'], 'px');
console.log(addPx('10'));
console.log(addPx('10px'));
console.log(addPx('10pt'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {anyPass, endsWith, unless, flip, concat, compose, map} = R;</script>
附录
为什么不使用默认参数?
我不知道在函数式编程中使用默认参数是否是一种推荐做法,但我意识到当您需要部分应用函数时,它们通常会妨碍您。
看看这个:
这里我们有一个将三个数相加的函数:
const foo = (a, b, c) => a + b + c;
foo(10, 20, 30); // 60
我们可以柯里化它并开始部分应用该函数:
const foo_curried = curry(foo);
foo_curried(10, 20, 30); // 60
foo_curried(10, 20)(30); // 60
foo_curried(10)(20, 30); // 60
foo_curried(10)(20)(30); // 60
让我们编写一个类似的函数,但使用默认参数。这按预期工作:
const bar = (a=10, b=20, c=30) => a + b + c;
bar(); // 60
bar(110); // 160
bar(110, 220); // 360
bar(110, 220, 330); // 660
但是,如果您想柯里化并部分应用它,则会出现错误:
const bar_curried = curry(bar);
bar_curried(); // 60
bar_curried(110); // 160
bar_curried(110)(220, 330); // Error!
为什么?柯里化函数会一直等到您提供了它的所有参数。在那之前它保持 returning 一个接受剩余参数的函数。但是,如果您部分应用具有默认参数的函数,那么您无法真正预测柯里化函数的结果 return:最终结果还是接受剩余参数的函数?
在最后一个示例中,bar_curried(110)
return 直接返回结果,即 110 + 20 + 30
,当您尝试将数字作为函数调用时会出现错误。
您也可以避免传递后缀列表,因为它看起来是多余的...只需确保给定的 value
不以数字结尾:
const ensureSuffix = R.curry((suffix, value) =>
R.unless(R.test(/\D$/), v => `${v}${suffix}`, value),
);
const addPx = ensureSuffix('px');
console.log(addPx(15));
console.log(addPx('15pt'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>