我正在尝试使用功能性方法来解决特定问题,作为学习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"))("t");
...但我错了,因为这不起作用,测试失败了:
1)将短的arg名称映射到long选项名称map short name to long 名字POINTFREE: TypeError:lookupByShortName不是函数 在Context.it.only(test / validator.spec.js:744:20)
所以我想到了另一个解决方案(不起作用,但我不明白为什么):
因为" t"是传递给R.propEq的第二个参数,使用R .__占位符,然后传入" t"最后:
let lookupByShortName = R.find(R.propEq("short", R.__))("t");
我已经在blog上完成了一系列文章,尽管我的理解更好,但我还没有。
你能否解释我出错的地方,谢谢。
答案 0 :(得分:3)
第一个问题是你的代码不起作用的原因。
最简单的解释方法是使用功能签名。
我们从propEq
开始:
propEq :: String -> a -> Object -> Boolean
这就像哈贝尔这样的语言。 propEq
是一个函数,它接受一个String并返回一个带有任意类型的函数的函数,并返回一个接受Object并返回一个Boolean的函数。您可以更明确地编写它,如
propEq :: String -> (a -> (Object -> Boolean))
您可以使用以下语法来调用它:
propEq('short')('t')({ long: "perky", short: "p" }); //=> false
Ramda的想法略有不同,即你不必一次传递这些。因此,有几种同样有效的方法可以调用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
,如下所示:
find :: (a -> Boolean) -> [a] -> a
由于类似的原因,这意味着其中一个在Ramda:
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
时,我们不会得到一个布尔值,而是从Object
到一个函数Boolean
。
这种类型不匹配是您当前方法不起作用的原因。
第二个问题是如何使其发挥作用。
最直接的方法是使用这样的东西:
let lookupByShortName = (abbrv, options) => R.find(R.propEq("short", abbrv), options);
lookupByShortName('t', options); //=> {"long": "turky", "short": "t"}
这是干净,清晰的代码。我可能会这样离开。但如果你真的希望它没有点,那么Ramda会为这种情况提供useWith
。您可以像这样使用它:
let lookupByShortName = R.useWith(R.find, [R.propEq('short'), R.identity]);
这可以看作是两个参数的(咖喱)函数。第一个参数传递给propEq('short')
,返回类型为(a -> Boolean)
的新函数,第二个参数传递给identity
,它不进行转换,只传递值不变。然后将这两个结果传递到find
useWith
和类似的converge
非常特定于Ramda。如果您不需要无点版本(例如,作为学习练习),可能会优先考虑第一个版本。