我对这里的答案有点困惑。我不知道他们是否在响应我实际上没有看到的要求,或者我是否遗漏了一些重要的东西。
但是,如果您只是想要一个装饰器,它将标量上的函数转换为对标量或标量数组进行操作的装饰器,那么这很简单,而且您离我们不远了。应该这样做:
const apply2Array = (fn) => (arg) =>
Array .isArray (arg) ? arg .map (fn) : fn (arg)
const convertF2C = (t) => (t - 32) / 1.8
const A = [5, 14, 23, 32, 41]
const B = 50
const arrayResult = apply2Array(convertF2C);
console .log (arrayResult (A))
console .log (arrayResult (B))
.as-console-wrapper {max-height: 100% !important; top: 0}
我建议您应该使用Array.isArray 进行检查,而不是使用map 属性。名为map 的属性可能不是Array.prototype.map,可能与制图有关。
其他 cmets 和答案建议您也希望在嵌套数组上使用相同的方法,将 [5, [[14, 23], 32], 41] 之类的内容转换为 [-15, [[-10, -5], 0], 5]。那不会更难。正如 Bergi 建议的那样,您需要做的就是将递归应用的函数包装在同一个装饰器中:
const apply2Array = (fn) => (arg) =>
Array .isArray (arg) ? arg .map (apply2Array (fn)) : fn (arg)
// ^^^^^^^^^^^
const convertF2C = (t) => (t - 32) / 1.8
const A = [5, 14, 23, 32, 41]
const B = 50
const C = [5, [[14, 23], 32], 41]
const arrayResult = apply2Array(convertF2C);
console .log (arrayResult (A))
console .log (arrayResult (B))
console .log (arrayResult (C))
.as-console-wrapper {max-height: 100% !important; top: 0}
不要这样做
不过,如果这个企业充满了潜在的陷阱,我还是建议这样做。例如,想象一下,您有一个对数字数组进行操作的 sum 函数,并且您想使用它对数字数组或数字数组进行操作。
如果您使用任一版本的apply2Array 将其包装起来,它将无法正常工作。在第一个版本中,如果您提供一个数字数组数组,该函数将按预期工作,但如果您只提供一个数字数组,该函数将失败。无论哪种方式,第二个都会失败。
问题在于,有时您的基本函数想要对数组进行操作。制作一个基于其输入类型执行多项操作的函数会使您失去一些简单性。
相反,我建议您创建多个函数来执行您需要的不同事情。您仍然可以使用装饰器,但比上述更通用。
这里我们使用一个叫做map,它具体化了Array.prototype.map:
const map = (fn) => (xs) =>
xs .map (x => fn (x))
const convertF2C = (t) => (t - 32) / 1.8
const convertAllF2C = map (convertF2C)
const A = [5, 14, 23, 32, 41]
const B = 50
console .log (convertAllF2C (A))
console .log (convertF2C (B))
.as-console-wrapper {max-height: 100% !important; top: 0}
如果你还想要深度映射,你可以重命名上面的装饰器,然后这样做:
const map = (fn) => (xs) =>
xs .map (x => fn(x))
const deepMap = (fn) => (arg) =>
Array .isArray (arg) ? arg .map (deepMap (fn)) : fn (arg)
const convertF2C = (t) => (t - 32) / 1.8
const convertAllF2C = map (convertF2C)
const deepConvertF2C = deepMap (convertF2C)
const A = [5, 14, 23, 32, 41]
const B = 50
const C = [5, [[14, 23], 32], 41]
const arrayResult = deepMap (convertF2C);
console .log (convertAllF2C (A))
console .log (convertF2C (B))
console .log (deepConvertF2C (C))
.as-console-wrapper {max-height: 100% !important; top: 0}
为您的三种情况调用三个单独的函数通常比一个函数更简单,该函数可以通过与三种不同风格的输出相关联的三种不同风格的输入来调用。由于它们是从我们的基本函数构建的,并且只使用了一些通用装饰器,因此它们仍然很容易维护。
但这不矛盾吗...?
有些人知道我是Ramda 的创始人和主要作者。 Ramda 有一个与此相关的map 函数。但它似乎可以对多种类型进行操作,包括数组、对象、函数等等。这不是矛盾吗?
我会说不。我们只需要向上移动一个抽象层。 FantasyLand 指定一个抽象泛型类型,Functor(借用抽象数学)。这些类型以某种方式包含另一种类型的一个或多个值,我们可以通过mapping 提供给这些值中的每一个的函数来创建类似结构的容器。您的map 函数必须遵守某些简单的规则才能将其视为 Functor,但如果您这样做,那么 Ramda 的 map 将与您的类型正常工作。换句话说,Ramda 的map 不适用于数组,但适用于任何 Functor。 Ramda 本身为数组、对象和函数提供了实现,但将对其他类型的调用委托给它们自己的 map 方法。
不过,基本的一点是,Ramda 在这里并没有真正增加额外的复杂性,因为 Ramda 的 map 的输入类型是 Functor 而不是 Array。
简单
函数式编程涉及很多方面。但中心主题之一必须是简单性。如果你还没有看过 Rich Hickey 的演讲 Simple Made Easy,我强烈推荐它。它解释了简单的客观概念,并描述了如何实现它。