具有多参数和条件的 Ramda

Ramda with multi parameter and conditions

我是函数式编程的新手,任何人都可以帮助我如何将此函数转换为最佳功能

const isNotNil = R.complement(R.isNil);
const isFunction = R.is(Function)

const a = (value, value2, fn) => {
   if (isNotNil(value)) {
     return isFunction(fn) ? fn(value) : value
   }
   return value2;
};

a(5,2, R.add(8))

在 FP 中,您应该尽可能地分解关注点,避免可选参数,而是使用更小的部分来驱动流程。

此外,Ramda 很有用,但缺少一些数据类型,例如 Maybe,在这种情况下可能非常有用。看看SanctuaryJS.

以下代码使用纯函数方法完全满足您的需求

const { toMaybe, maybe, pipe } = sanctuary

const a = f => value => value2 => pipe ([
    toMaybe, // converts to Just (value) or Nothing, when nil
    maybe (value2) (f) // if Nothing, value2, otherwise, value is passed to f
]) (value)

// Output: 3
const output1 = a (x => x + 1) (2) (4)
console.log ('output1: ', output1)

// Output: 4
const output2 = a (x => x + 1) (null) (4)
console.log ('output2: ', output2)
<script src="https://bundle.run/sanctuary@0.15.0"></script>

请注意,我没有检查 fFunction。 JavaScript 是一种动态类型的语言:假设它将成为 Function,如果不是,则快速失败。

利用部分应用的重构方法:

const { toMaybe, maybe, pipe } = sanctuary

// Re-arranged parameters: now value is the latest one.
const a = f => value2 => pipe ([
    toMaybe, // converts to Just (value) or Nothing, when nil
    maybe (value2) (f) // if Nothing, value2, otherwise, value is passed to f
])

// Output: 3
const output1 = a (x => x + 1) (4) (2)
console.log ('output1: ', output1)

// Output: 4
const output2 = a (x => x + 1) (4) (null)
console.log ('output2: ', output2)
<script src="https://bundle.run/sanctuary@0.15.0"></script>

在 Ramda 中,您倾向于将要操作的数据作为函数的最后一个参数传递。所有其他参数都可以看作是应用于数据的过程的配置。

知道了这一点,您的函数签名可能应该如下所示:a(defaultValue, func, value)

这允许您构建具有预定义行为的柯里化函数:

const incOr42 = a(42, inc);
incOr42(1);    // 2
incOr42(null); // 42

现在让我们把问题分解成更小的部分:

首先,让我们验证一下 func,它本身可能是一个函数:

如果给 checkFn 的东西不是一个函数,它 return 就是一个函数,它总是 return 它的参数。否则它 return 是原始函数

const checkFn = unless(is(Function), always(identity));
checkFn(add(8))(5); // 13
checkFn('foo')(5);  // 5

其次,让我们构建一个接受两个参数的函数:defaultValuefunc。 它将 return 接受 value 和 return 的函数 defaultValue 如果 value 为零,否则将 func 应用于它:

const a = useWith(ifElse(isNil), [always, checkFn]);
const incOr42 = a(42, inc);
incOr42(1);    // 2
incOr42(null); // 42

把所有东西放在一起:

const {unless, is, always, identity, ifElse, isNil, useWith, inc} = R;

const checkFn = unless(is(Function), always(identity));
const a = useWith(ifElse(isNil), [always, checkFn]);

const incOr42 = a(42, inc);

console.log(incOr42(1));
console.log(incOr42(null));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

我真的看不出有什么理由超越显而易见的范围。

这个版本似乎完全符合您的要求,而且可读性很强:

const {is, isNil} = R

const a = (val, val2, fn) => isNil(val) ? val2 : is(Function, fn) ? fn(val) : val

console.log(a(5, 2, R.add(8)))       //=> 13
console.log(a(5, 2, 'NonAFunction')) //=> 5
console.log(a(null, 2, R.add(8)))    //=> 2
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

你基本上是在发明 Option 类型;有时称为 Maybe。使用像 daggy 这样的简单标记联合库,我们可以实现 Option -

const daggy = require('daggy')

const Option = daggy.taggedSum('Option', {
  Some: ['x'],
  None: [],
})

const { Some, None } = Option

Option.prototype.map = function(f) {
  return this.cata({
    Some: x => Some(f(x)),
    None: _ => this
  })
}

const add = x => y => x + y

console.log
  ( Some(1).map(add(10)) // Some(11)
  , None.map(add(10)) // None
  )

我们可以添加一个withDefault方法让我们回到正常值-

Option.prototype.withDefault = function (x) {
  return this.cata({
    Some: x => x,
    None: _ => x
  })
}

console.log
  ( Some(1).map(add(10)).withDefault(0) // 11
  , None.map(add(10)).withDefault(0) // 0
  )

最后,一个将普通值转换为我们新的选项类型的构造函数 -

Option.fromNullable = function (x) {
  if (x == null)
    return None
  else
    return Some(x)
}

console.log
  ( Option.fromNullable(1).map(add(10)).withDefault(0) // 11
  , Option.fromNullable(null).map(add(10)).withDefault(0) // 0
  )

如果您仍然需要将此表达式表示为一个函数,例如您问题中的 a -

const a = R.curry((f, x, y) =>
  Option.fromNullable(x).map(f).withDefault(y))

console.log
  ( a (R.add(8), 5, 2) // 13
  , a (R.add(8), null, 2) // 2
  )

Ramda 不包含内置的 OptionMaybe 但如果您正在寻找现有的实现,则有npm 上的流行模块,例如 data/maybe.