如何改进我编写的 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>