12

我正在学习Ramda,我有点困惑如何lodash使用Ramda. Ramda返回其操作的函数而不是实际值,这似乎是函数式编程的重点,但是在此示例中,我有第二个参数localRegex不是主要参数。Ramda如果不包装函数并使用.apply().call()将包装的函数参数传播给函数,似乎不可能完全复制Ramda它,这似乎比使用更复杂lodash

var _ = require("lodash")
var R = require("ramda")

var localRegex = /^.\.\/|^.\/|^\//

function getRecursiveDeps(deps, localRegex){
  return _.chain(deps)
    .map(function(dep){
      return dep.source.value
    })
    .filter(function(dep){
      return dep.match(localRegex)
    })
    .value()
}

var items = [
  {
    "source": {
      "value": "./foo"
    }
  },
  {
    "source": {
      "value": "bar"
    }
  }
]

console.log(getRecursiveDeps(items, localRegex))

这就是我所拥有的,但它不起作用。

var getRecursiveDeps = R.chain(
  R.map(function(dependency){
    return dependency.source.value
  }),
  R.filter(function(value){
    return value.match(localRegex)
  })
)

有没有办法Ramda使用主变量进行链接并向下传递localRegex?有没有办法复制使用的getRecursiveDeps那个?lodashRamda

有很多关于Ramda功能性和underscore非功能性的讨论lodash。但在这种情况下,getRecursiveDeps是一个从 中返回值的函数lodash。当您创建这样的函数lodashunderscore结果相同时,在包装它时还有更多工作,在这种情况下,使用Ramdaover会有什么好处Lodash

4

2 回答 2

10

R.chain做一些与 完全不同的事情_.chain。根据当前文档,它的类型是(a -> [b]) -> [a] -> [b],尽管它的实际类型更通用。将其视为“平面地图”功能。

您在这里真正想要的是R.compose或其从左到右的等价物R.pipe

如果函数的目标是找到本地依赖项,那么将模式嵌入函数中似乎是合适的。我会这样写:

// getLocalDeps :: [{ source :: { value :: String }}] -> [String]
const getLocalDeps =
R.pipe(R.map(R.path(['source', 'value'])),
       R.filter(R.test(/^[.]{0,2}[/]/)));

getLocalDeps(items);  // => ['./foo']

我对这个名字有点困惑,getRecursiveDeps因为这个函数不是递归的。getLocalDeps似乎更合适。


如果你想参数化模式,我建议getLocalDeps分成更小的部分:

// isLocal :: String -> Boolean
const isLocal = R.test(/^[.]{0,2}[/]/);

// getDeps :: [{ source :: { value :: String }}] -> [String]
const getDeps = R.map(R.path(['source', 'value']));

// getLocalDeps :: [{ source :: { value :: String }}] -> [String]
const getLocalDeps = R.pipe(getDeps, R.filter(isLocal));

然后,您可以根据这些构建块定义其他功能:

// getNonLocalDeps :: [{ source :: { value :: String }}] -> [String]
const getNonLocalDeps = R.pipe(getDeps, R.reject(isLocal));

// getLocalJsonDeps :: [{ source :: { value :: String }}] -> [String]
const getLocalJsonDeps = R.pipe(getLocalDeps, R.filter(R.test(/[.]json$/)));
于 2015-07-29T15:26:06.583 回答
6

另一种方法是,免积分并保留现有 API,如下所示:

// getLocalDeps :: [{ source :: { value :: String }}] -> RegExp -> [String]
const getLocalDeps =  R.useWith(
  R.flip(R.call),
  R.map(R.path(['source', 'value'])),
  R.pipe(R.unary(R.test), R.filter)
);

localDeps(items, localRegex); //=> ["./foo"]

函数的最后一行让我觉得有点不幸,这个问题让我提出了一个关于恢复库的一些最近更改的问题。有几种变体可以使用:

// ...
R.pipe(regex => item => R.test(regex, item), R.filter)
//...

或者

// ...
regex => R.filter(R.test(regex))
//...

但在最近对 Ramda 进行更改之前,它本来只是

// ...
R.pipe(R.test, R.filter)
//...

不过,有一件事是 Ramda 努力使参数保持逻辑顺序:那些不太可能改变的参数在那些更可能改变的参数之前出现。考虑到这一点,我更喜欢这样的东西:

// getLocalDeps :: RegExp -> [{ source :: { value :: String }}] -> [String]
var getLocalDeps2 =  R.useWith(
  R.call,
  R.pipe(R.unary(R.test), R.filter),
  R.map(R.path(['source', 'value']))
);

localDeps2(localRegex, items); //=> ["./foo"]

感觉更干净了。此外,它允许您预定义函数并单独使用它:

myDeps = localDeps2(localRegex);
myDeps(items); //=> ["./foo"]

这是 Ramda 的一个很好的部分。

于 2015-07-29T19:20:15.187 回答