Javascript 如何正确使用Ramda/JS组合函数

Javascript 如何正确使用Ramda/JS组合函数,javascript,functional-programming,ramda.js,Javascript,Functional Programming,Ramda.js,作为学习Ramda.js练习的一部分,我正在尝试使用函数方法来解决特定问题 所以我有一个测试: it.only("map short name to long name POINTFREE", () => { let options = [ { long: "perky", short: "p" }, { long: "turky", short: "t" } ]; let lookupByShortName = R.find(R.propEq("short", "t"));

作为学习Ramda.js练习的一部分,我正在尝试使用函数方法来解决特定问题

所以我有一个测试:

it.only("map short name to long name POINTFREE", () => {
  let options = [ { long: "perky", short: "p" }, { long: "turky", short: "t" } ];

  let lookupByShortName = R.find(R.propEq("short", "t"));
  let result = lookupByShortName(options);
  expect(result).to.have.property("long", "turky");
});
“选项”用作查找序列。我需要转换一系列指定为单个字符的字符串,通过引用选项序列将其转换为更长的等效名称。因此,字符“t”应转换为选项中定义的“turky”

然而,这并不是我所需要的有用的结构。函数“lookupByShortName”不是通用函数,它是用值“t”硬编码的。我想要的是省略“t”参数,这样当您调用lookupByShortName时,因为它应该是curry(通过R.find),所以它应该返回一个需要缺少参数的函数。因此,如果我这样做,测试将失败:

let lookupByShortName = R.find(R.propEq("short"));
在这里,lookupByShortName应该成为一个需要一个缺少的参数的函数,所以理论上,我认为我应该能够调用这个函数,如下所示:

lookupByShortName("t")
或者更具体地说(在末尾附加“t”):

。。。但我错了,因为这不起作用,测试失败:

let lookupByShortName = R.find(R.propEq("short"));
1) 将短参数名称映射为长选项名称将短名称映射为长 名称无点: TypeError:lookupByShortName不是函数 仅限于Context.it(test/validator.spec.js:744:20)

因此,我想到了另一个解决方案(它不起作用,但我不明白为什么):

由于“t”是传递给R.propEq的第二个参数,请使用R.uuuu占位符,然后在末尾传递“t”:

let lookupByShortName = R.find(R.propEq("short"))("t");
let lookupByShortName = R.find(R.propEq("short", R.__))("t");
我已经阅读了一系列关于a的文章,虽然我的理解更好,但我还没有做到


谢谢,你能告诉我哪里出了问题吗。

第一个问题是为什么你的代码不起作用

解释这一点的最简单方法是使用函数签名

我们从
propEq
开始:

propEq :: String -> a -> Object -> Boolean
在哈克尔这样的语言中,情况就是这样
propEq
是一个函数,它接受字符串并返回一个接受任意类型的函数,返回一个接受对象并返回布尔值的函数。你可以把它写得更明确,比如

propEq :: String -> (a -> (Object -> Boolean))
您可以使用如下语法将其称为:

propEq('short')('t')({ long: "perky", short: "p" }); //=> false
拉姆达有一个稍微不同的想法,即你不必一次通过这些考试。因此,有几种同样有效的方法来调用Ramda函数:

propEq :: String -> (a -> (Object -> Boolean))
          String -> ((a, Object) -> Boolean)
          (String, a) -> (Object -> Boolean)
          (String, a, Object) -> Boolean
这分别意味着这样称呼它:

propEq('short')('t')({ long: "perky", short: "p" }); //=> false
propEq('short')('t', { long: "perky", short: "p" }); //=> false
propEq('short', 't')({ long: "perky", short: "p" }); //=> false
propEq('short', 't', { long: "perky", short: "p" }); //=> false
find :: (a -> Boolean) -> [a] -> a 
let lookupByShortName = (abbrv, options) => R.find(R.propEq("short", abbrv), options);
lookupByShortName('t', options); //=> {"long": "turky", "short": "t"}
let lookupByShortName = R.useWith(R.find, [R.propEq('short'), R.identity]);
接下来,我们将看到
find
,如下所示:

propEq('short')('t')({ long: "perky", short: "p" }); //=> false
propEq('short')('t', { long: "perky", short: "p" }); //=> false
propEq('short', 't')({ long: "perky", short: "p" }); //=> false
propEq('short', 't', { long: "perky", short: "p" }); //=> false
find :: (a -> Boolean) -> [a] -> a 
let lookupByShortName = (abbrv, options) => R.find(R.propEq("short", abbrv), options);
lookupByShortName('t', options); //=> {"long": "turky", "short": "t"}
let lookupByShortName = R.useWith(R.find, [R.propEq('short'), R.identity]);
出于类似的原因,这意味着在拉姆达:

find :: (a -> Boolean) -> ([a] -> a)
     :: ((a -> Boolean), [a]) -> a
当你打电话的时候

find(propEq('short'))
您正试图将
a->Object->Boolean
作为第一个参数传递给
find
,它希望将
a->Boolean
作为第一个参数。虽然Javascript不是强类型的,而且Ramda也没有尝试提供太多强类型的帮助,但是您有一个类型不匹配的问题。事实上,您已经沉没了,尽管Ramda将接受您的函数,就像它可以工作一样,并返回类型为
[a]->a
的函数。但是这个函数不能正常工作,因为
find
所做的是将
[a]
中的每个
a
传递到我们的
propEq('short')
,直到其中一个返回
true
。这永远不会发生,因为
propEq('short')
的签名是
a->Object->Boolean
,所以当我们传递
a
时,我们不会得到Boolean,而是从
Object
Boolean
的函数

这种类型不匹配是当前方法不起作用的原因


第二个问题是如何让它发挥作用

最直接的方法是使用如下内容:

propEq('short')('t')({ long: "perky", short: "p" }); //=> false
propEq('short')('t', { long: "perky", short: "p" }); //=> false
propEq('short', 't')({ long: "perky", short: "p" }); //=> false
propEq('short', 't', { long: "perky", short: "p" }); //=> false
find :: (a -> Boolean) -> [a] -> a 
let lookupByShortName = (abbrv, options) => R.find(R.propEq("short", abbrv), options);
lookupByShortName('t', options); //=> {"long": "turky", "short": "t"}
let lookupByShortName = R.useWith(R.find, [R.propEq('short'), R.identity]);
这是干净、清晰的代码。我可能就这样离开了。但是如果你真的希望它是无点数的,Ramda为这种情况提供了最新的解决方案。您可以这样使用它:

propEq('short')('t')({ long: "perky", short: "p" }); //=> false
propEq('short')('t', { long: "perky", short: "p" }); //=> false
propEq('short', 't')({ long: "perky", short: "p" }); //=> false
propEq('short', 't', { long: "perky", short: "p" }); //=> false
find :: (a -> Boolean) -> [a] -> a 
let lookupByShortName = (abbrv, options) => R.find(R.propEq("short", abbrv), options);
lookupByShortName('t', options); //=> {"long": "turky", "short": "t"}
let lookupByShortName = R.useWith(R.find, [R.propEq('short'), R.identity]);
这可以看作是两个参数的(curried)函数。第一个参数传递给
propEq('short')
,返回类型为
(a->Boolean)
的新函数,第二个参数传递给
identity
,它不进行转换,只完整传递值。然后将这两个结果传递到
find


useWith
和类似的功能非常特定于Ramda。如果你不需要点免费版本(例如,作为一个学习练习),这个版本的第一个版本可能是更好的。

谢谢史葛的回答,我需要一些时间来考虑和正确地理解它,在我正确地评论之前。为现在干杯。。。