在尝试以函数样式编写代码时,我经常会遇到这样的场景:我想使用两个函数将一个集合或流映射到另一个集合:一个函数应用于传递给定谓词的元素,另一个函数应用于元素失败。这两个函数都会生成一个公共输出,应该收集这些输出以供进一步处理。
这是Java中一个人为的例子。
Stream.of("foo", "bar", "baz", "qux")
.map(it -> it.startsWith("ba") ? it.toUpperCase() : new StringBuilder(it).reverse())
.forEach(System.out::println);
和Groovy中的相同示例。
["foo", "bar", "baz", "qux"]
.collect{ it.startsWith("ba") ? it.toUpperCase() : it.reverse() }
.each{ println(it) }
我的问题是关于用于实现此映射的条件(三元)语句。是否存在实现这种映射的功能习惯用法而不诉诸分支逻辑?
答案 0 :(得分:1)
您始终可以使用其他函数对表达式进行抽象。由于您标记了语言无关的问题,因此我使用Javascript对您的示例进行编码,可在您喜欢的浏览器中执行 - 我希望这没关系:
// select :: (a -> Boolean, a -> b, a -> b) -> a -> b
const select = (p, f, g) => x => p(x) ? f(x) : g(x);
// mapSelect :: (a -> Boolean, a -> b, a -> b) -> [a] -> [b]
const mapSelect = (p, f, g) => xs => xs.map(select(p, f, g));
const reverse = s => s.split("").reverse().join("");
const toUpperCase = s => s.toUpperCase();
const startsWith = s => t => t.indexOf(s) === 0
const xs = ["foo", "bar", "baz", "qux"];
console.log(
mapSelect(startsWith("ba"), toUpperCase, reverse) (xs)
);

显然,mapSelect
并没有将您从条件运算符中拯救出来。它只是另一层抽象。由于它不像map
那么常见,所以尤其会增加同事的认知负担而不会增加太多。
结论:我会坚持使用显式条件运算符,而不是伪装分支逻辑。