Ramda.js 管道,根据前一个参数设置 属性
Ramda.js pipe that sets a property based in a previous parameter
目前,我有以下代码(有效):
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>
但是我认为由于每个管道函数末尾的 (obj)
,我想我可以在 "Ramda world" 中将其重构为更好的东西。
我还是这个库的新手,所以我还不知道所有的方法和技巧。
使用 Ramda 有更好的方法吗?
我的"real"代码是这样的:
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 })
);
}
我认为您可能在这里过度使用了 Ramda。代码有点混乱。这可能在将来更容易阅读并且更易于维护,同时仍然可以使用:
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 }))
}
或者,如果您真的想保留这些设置器,请尝试将您的 addPathAndRunProps
函数拆分为两个设置器:
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 }))
}
在这两种情况下,我都删除了您的 addModuleRunAndFilterInvalid
函数。将 addModuleRunAndFilterInvalid
拆分成它自己的函数并没有使您的函数更加清晰,返回管道的结果阐明了 getScripts
函数本身的目的。
此外,在您的代码中,您不断调用正在操作的对象 dir
。这是令人困惑的,因为它暗示对象在每次函数调用时都具有相同的结构。但是,传递给 addRunProp
的变量与传递给 addPathProp
的变量的结构不同(传递给 addRunProp
的变量需要一个额外的 path
属性)。要么想出一个描述性的名称,要么只使用 x
。您可以将 x
视为您的函数正在运行的对象。要弄清楚 x
是什么,请查看函数名称(例如 addRunProp
表示 x
是将添加 运行 属性 的东西) .
另一个可能有用的提示:我已经确定了 aug
("augment" 的缩写)的命名约定,用于向对象添加 属性 或一些信息。所以我会重命名你的 addPathProp
函数 augPath
并重命名你的 addRunProp
函数 augRun
。因为我一直使用它,所以我知道当我在函数开头看到 aug
时,它会添加一个 属性.
我同意 Cully 的回答——在这里尝试使用 Ramda 的函数可能没有任何充分的理由。
但是,如果您有兴趣,可以选择使用一些 Ramda 函数。
chain
and ap
are fairly generic functions operating on two different abstract types。但是当与函数一起使用时,它们作为组合器具有一些相当有用的行为:
chain (f, g) (x) //=> f (g (x)) (x)
ap (f, g) (x) //=> f (x) (g (x))
这意味着您可以这样编写函数:
const piped = R.pipe(
chain (assoc ('b'), pipe (prop ('a'), double)),
chain (assoc ('c'), pipe (prop ('b'), double)),
)
我不认为这个版本比原来有改进;这些内部 pipe
调用中涉及的重复太复杂了。
然而,使用辅助函数,这可能更合理:
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')),
);
现在,在我看来,这是非常易读的代码。当然它需要了解chain
以及它如何应用于功能,但有了它,我认为它实际上是对原始的改进。
我经常指出,只有当无点代码使我们的代码更具可读性时,它才是有用的工具。当它不指向时,代码的功能不亚于无指向。
顺便说一句,我只想说,我对你的问题的质量印象深刻。阅读经过深思熟虑和精心呈现的问题真的很棒。谢谢!
目前,我有以下代码(有效):
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>
但是我认为由于每个管道函数末尾的 (obj)
,我想我可以在 "Ramda world" 中将其重构为更好的东西。
我还是这个库的新手,所以我还不知道所有的方法和技巧。
使用 Ramda 有更好的方法吗?
我的"real"代码是这样的:
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 })
);
}
我认为您可能在这里过度使用了 Ramda。代码有点混乱。这可能在将来更容易阅读并且更易于维护,同时仍然可以使用:
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 }))
}
或者,如果您真的想保留这些设置器,请尝试将您的 addPathAndRunProps
函数拆分为两个设置器:
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 }))
}
在这两种情况下,我都删除了您的 addModuleRunAndFilterInvalid
函数。将 addModuleRunAndFilterInvalid
拆分成它自己的函数并没有使您的函数更加清晰,返回管道的结果阐明了 getScripts
函数本身的目的。
此外,在您的代码中,您不断调用正在操作的对象 dir
。这是令人困惑的,因为它暗示对象在每次函数调用时都具有相同的结构。但是,传递给 addRunProp
的变量与传递给 addPathProp
的变量的结构不同(传递给 addRunProp
的变量需要一个额外的 path
属性)。要么想出一个描述性的名称,要么只使用 x
。您可以将 x
视为您的函数正在运行的对象。要弄清楚 x
是什么,请查看函数名称(例如 addRunProp
表示 x
是将添加 运行 属性 的东西) .
另一个可能有用的提示:我已经确定了 aug
("augment" 的缩写)的命名约定,用于向对象添加 属性 或一些信息。所以我会重命名你的 addPathProp
函数 augPath
并重命名你的 addRunProp
函数 augRun
。因为我一直使用它,所以我知道当我在函数开头看到 aug
时,它会添加一个 属性.
我同意 Cully 的回答——在这里尝试使用 Ramda 的函数可能没有任何充分的理由。
但是,如果您有兴趣,可以选择使用一些 Ramda 函数。
chain
and ap
are fairly generic functions operating on two different abstract types。但是当与函数一起使用时,它们作为组合器具有一些相当有用的行为:
chain (f, g) (x) //=> f (g (x)) (x)
ap (f, g) (x) //=> f (x) (g (x))
这意味着您可以这样编写函数:
const piped = R.pipe(
chain (assoc ('b'), pipe (prop ('a'), double)),
chain (assoc ('c'), pipe (prop ('b'), double)),
)
我不认为这个版本比原来有改进;这些内部 pipe
调用中涉及的重复太复杂了。
然而,使用辅助函数,这可能更合理:
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')),
);
现在,在我看来,这是非常易读的代码。当然它需要了解chain
以及它如何应用于功能,但有了它,我认为它实际上是对原始的改进。
我经常指出,只有当无点代码使我们的代码更具可读性时,它才是有用的工具。当它不指向时,代码的功能不亚于无指向。
顺便说一句,我只想说,我对你的问题的质量印象深刻。阅读经过深思熟虑和精心呈现的问题真的很棒。谢谢!