Javascript 用ramda实现JS中的无点递归

Javascript 用ramda实现JS中的无点递归,javascript,recursion,functional-programming,pointfree,ramda.js,Javascript,Recursion,Functional Programming,Pointfree,Ramda.js,我正在学习无点函数,并尝试以这种方式实现这个递归空清除器 工作正常,但不是无点的: function removeNulls(obj) { return R.ifElse( R.either(R.is(Array), R.is(Object)), R.pipe( R.filter(R.pipe(R.isNil, R.not)), R.map(removeNulls) ), R.identity )(obj) } module.ex

我正在学习无点函数,并尝试以这种方式实现这个递归空清除器

工作正常,但不是无点的:

function removeNulls(obj) {
  return R.ifElse(
    R.either(R.is(Array), R.is(Object)),
    R.pipe(
      R.filter(R.pipe(R.isNil, R.not)),
      R.map(removeNulls)
    ),
    R.identity
  )(obj)
}

module.exports = removeNulls
以下是我的无效尝试:

const removeNulls = R.ifElse(
  R.either(R.is(Array), R.is(Object)),
  R.pipe(
    R.filter(R.pipe(R.isNil, R.not)),
    // throws `ReferenceError: removeNulls is not defined`
    R.map(removeNulls)
  ),
  R.identity
)

Javascript缺乏惰性是这里的双重杀手: 当它的常量为时不能调用它,因为您在同一范围内,并且它试图在其定义中解析resolveNulls

此外,您不能只映射(recurseAction(action)),因为定义本身会破坏堆栈,所以您需要将其包装到另一个作用域中:

const {ifElse, always, tap, apply, either, is, isNil, not, pipe, filter, map, identity} = require('ramda')

const filterNotNull = filter(pipe(isNil, not))
const log = tap(console.log)

const recurseAction =
  action =>
    ifElse(
      either(is(Array), is(Object)),
      pipe(
        action,
        map(a => recurseAction(action)(a))
      ),
      identity
    )

const removeNulls = recurseAction(filterNotNull)

const a = {
  a: null,
  b: "blah",
  c: 2,
  d: undefined,
  e: {
    meow: null,
    blah: undefined,
    jim: 'bob'
  }
}

const b = removeNulls(a)
console.log(b)

幸运的是,JavaScript有足够的资源来解决它缺乏惰性的问题。因此,完全可以通过以下方式使用lambda函数声明递归无点解决方案:
a=>f(a)
。只需将
R.map(removeNull)
替换为
R.map(a=>removeNull(a))

在您的情况下,我建议您使用
R.reject
,这是
R.filter
的操作站点。由于要对谓词求反,
R.filter(R.pipe(R.isNil,R.not))
等于
R.reject(R.isNil)

最后,此函数具有以下结构
ifElse(谓词,whenTrue,identity)
,它等于
when(谓词,whenTrue)

简化版,关于Declan Whelan的评论,因为数组是对象

const removeNulls = R.when(
    R.is(Object),
    R.pipe(
        R.reject(R.isNil),
        R.map(a => removeNulls(a))
    )
)

您不能,因为JavaScript不提供延迟声明参数的方法。您可能会幸运地使用
Y
combinator,但这会变得非常糟糕。@Bergi谢谢您的提示,我将保持原样。我想了解更多关于使用Y组合器的知识,如果只是为了将来使用的话。看起来是这样,但我不知道从那以后该怎么办。用谷歌搜索有点困难,因为…@Bergi是对的,你不能用const。。。但是,如果将操作与应用分离,则可以执行此操作:const filterNotNull=filter(pipe(isNil,not))const recurseAction=action=>ifElse(is(Array),is(Object)),pipe(action,map(action)),identity)@NigelBenns您的
recurseAction
不是无点的,而且它不是递归的-它应该是
map(recurseAction(action))
在JS中实现一个Y-combinator版本很容易。但这并不实用,因为数组是一个对象,所以可以稍微简化一下:const removeNulls=R.when(R.is(Object)、R.pipe(R.reject(R.isNil)、R.map(A=>removeNulls(A))),但这并不是无点的,OPs的主要需求并没有得到满足。不过优化效果不错。:)@是的is@yosbel地图中的
a
是一个点。如果您只使用
R.map(removeNulls)
它将不起作用。@jlouzado因此这是语言的一个限制,答案是语言允许的无点。你的建议是什么?
const removeNulls = R.ifElse(
    R.either(R.is(Array), R.is(Object)),
    R.pipe(
        R.reject(R.isNil),
        R.map(a => removeNulls(a))
    ),
    R.identity
)
const removeNulls = R.when(
    R.either(R.is(Array), R.is(Object)),
    R.pipe(
        R.reject(R.isNil),
        R.map(a => removeNulls(a))
    )
)
const removeNulls = R.when(
    R.is(Object),
    R.pipe(
        R.reject(R.isNil),
        R.map(a => removeNulls(a))
    )
)