简体   繁体   English

基于前一个参数设置属性的 Ramda.js 管道

[英]Ramda.js pipe that sets a property based in a previous parameter

Currently, I have the following code (which works):目前,我有以下代码(有效):

 const double = R.multiply(2); const piped = R.pipe( (obj) => R.assoc('b', double(obj.a))(obj), (obj) => R.assoc('c', double(obj.b))(obj) ); console.log( piped({ a: 1 }) );
 <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

However I think that due to that (obj) at the end of each pipe function, I guess that I could refactor it to something better in the "Ramda world".但是我认为由于每个管道函数末尾的(obj) ,我想我可以将它重构为“Ramda 世界”中更好的东西。

I'm still new to this library, so I yet don't know all the methods and tricks.我还是这个库的新手,所以我还不知道所有的方法和技巧。

Is there a better way to do so using Ramda?使用 Ramda 有没有更好的方法来做到这一点?

My "real" code is this:我的“真实”代码是这样的:

function getScripts() {
  const tryRequire = tryCatch((path) => require(path).run, always(null));

  const addPathAndRunProps = pipe(
    // Note: The `scriptsPath` function is a bound path.join function.
    // It just returns a string.
    (dir) => assoc('path', scriptsPath(dir.name, 'index.js'))(dir),
    (dir) => assoc('run', tryRequire(dir.path))(dir)
  );

  const addModuleRunAndFilterInvalid = pipe(
    map(addPathAndRunProps),
    filter((dir) => typeof dir.run === 'function')
  );

  return addModuleRunAndFilterInvalid(
    fs.readdirSync(SCRIPTS_PATH, { withFileTypes: true })
  );
}

I think you might be over-using Ramda here.我认为您可能在这里过度使用 Ramda。 The code is a bit confusing.代码有点混乱。 This would likely be easier to read in the future and more maintainable, while still being functional:这在未来可能更容易阅读,更易于维护,同时仍然有效:

function getScripts() {
  const tryRequire = tryCatch((path) => require(path).run, always(null));

  const addPathAndRunProps = dir => {
    const path = scriptsPath(dir.name, 'index.js')

     return {
        ...dir,
        path,
        run: tryRequire(path),
     }
  }

  return pipe(
     map(addPathAndRunProps),
     filter(x => typeof x.run === 'function'),
  )(fs.readdirSync(SCRIPTS_PATH, { withFileTypes: true }))
}

Or, if you really want to keep those setters, try splitting your addPathAndRunProps function into two setters:或者,如果您真的想保留这些 setter,请尝试将addPathAndRunProps函数拆分为两个 setter:

function getScripts() {
  const tryRequire = tryCatch((path) => require(path).run, always(null));

  const addPathProp = x => assoc('path', scriptsPath(x.name, 'index.js'), x)
  const addRunProp = x => assoc('run', tryRequire(x.path), x)

  return pipe(
    map(addPathProp),
    map(addRunProp),
    filter(x => typeof x.run === 'function'),
  )(fs.readdirSync(SCRIPTS_PATH, { withFileTypes: true }))
}

In both cases, I got rid of your addModuleRunAndFilterInvalid function.在这两种情况下,我都摆脱了您的addModuleRunAndFilterInvalid函数。 It doesn't add any clarity to your function to have addModuleRunAndFilterInvalid split out into its own function, and returning the result of the pipe clarifies the purpose of the getScripts function itself.addModuleRunAndFilterInvalid拆分为自己的函数并不会为您的函数增加任何清晰度,并且返回管道的结果阐明了getScripts函数本身的用途。

Also, in your code, you keep calling the object you're operating on dir .此外,在您的代码中,您不断调用正在dir操作的对象。 This is confusing since it implies the object has the same structure on each function call.这是令人困惑的,因为它意味着对象在每个函数调用中都具有相同的结构。 However the variable passed to addRunProp does not have the same structure as what is passed to addPathProp (the one passed to addRunProp requires an extra path prop).然而,传递给addRunProp的变量与传递给addRunProp的变量的结构addPathProp (传递给addRunProp需要一个额外的path道具)。 Either come up with a descriptive name, or just use x .要么想出一个描述性的名称,要么只使用x You can think of x as the thing your function is operating on.您可以将x视为您的函数正在运行的对象。 To figure out what x is, look at the function name (eg addRunProp means that x is something that will have a run property added to it).要弄清楚x是什么,请查看函数名称(例如addRunProp意味着x将添加一个 run 属性)。

One other potentially useful tip: I've settled on the naming convention of aug (short of "augment") for adding a property or bit of info to an object.另一个可能有用的提示:我已经确定了aug的命名约定(简称“增强”),用于向对象添加属性或信息位。 So I'd rename your addPathProp function augPath and rename your addRunProp function augRun .所以我会重命名您的addPathProp函数augPath并重命名您的addRunProp函数augRun Since I use it consistently, I know that when I see aug at the beginning of a function, it's adding a property.由于我一直使用它,我知道当我在函数开头看到aug时,它正在添加一个属性。

I agree with Cully's answer -- there might not be any good reason to try to use Ramda's functions here.我同意 Cully 的回答——在这里尝试使用 Ramda 的函数可能没有任何充分的理由。

But, if you're interested, there are some Ramda functions which you might choose to use.但是,如果您有兴趣,可以选择使用一些 Ramda 函数。

chain and ap are fairly generic functions operating on two different abstract types . chainap是在两种不同的抽象类型上运行的相当通用的函数。 But when used with functions, they have some fairly useful behavior as combinators:但是当与函数一起使用时,它们作为组合器有一些相当有用的行为:

chain (f, g) (x) //=> f (g (x)) (x)
ap (f, g) (x)    //=> f (x) (g (x))

That means that you could write your function like this:这意味着您可以像这样编写函数:

const piped = R.pipe(
  chain (assoc ('b'), pipe (prop ('a'), double)),
  chain (assoc ('c'), pipe (prop ('b'), double)),
)

I don't think this version improves on the original;我不认为这个版本对原始版本有改进; the repetition involved in those internal pipe calls is too complex.这些内部pipe调用中涉及的重复太复杂了。

However with a helper function, this might be more reasonable:但是使用辅助函数,这可能更合理:

const doubleProp = curry (pipe (prop, double))
// or doubleProp = (prop) => (obj) => 2 * obj[prop]

const piped = R.pipe(
  chain (assoc ('b'), doubleProp ('a')),
  chain (assoc ('c'), doubleProp ('b')),
);

This is now, to my mind, pretty readable code.现在,在我看来,这是非常易读的代码。 Of course it requires an understanding of chain and how it applies to functions, but with that, I think it's actually an improvement on the original.当然,它需要了解chain以及它如何应用于功能,但是我认为它实际上是对原始的改进。

I frequently make the point that point-free code is a useful tool only when it makes our code more readable.我经常指出,只有当我们的代码更具可读性时,无点代码才是有用的工具。 When it doesn't pointed code is no less functional than point-free.当它没有指向代码时,它的功能并不比无指向性差。


By the way, I just want to note that I'm impressed with the quality of your question.顺便说一句,我只想指出,我对您问题的质量印象深刻。 It's really nice to read questions that are well-thought out and well-presented.阅读经过深思熟虑和精心呈现的问题真的很棒。 Thank you!谢谢!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM