高阶编程
本周,该章名为“前进”。 样式的约束不是直接调用函数,而是将其作为参数传递给下一个函数,稍后再调用。
这是5在编程风格焦点series.Other职位练习日交包括:
我不确定高阶函数的概念是否起源于函数式编程。 但是,这个概念本身很容易掌握:高阶函数可以用作另一个函数的函数参数或返回值。 从版本8开始,它在Java中可用,但是从其开始就在Scala和Kotlin中可用。
在Java中,它们主要与流结合使用:
Stream.of("A","B","C","D","E").map(newFunction<String,String>(){&#64;OverridepublicStringapply(Strings){returns.toLowerCase();}});}Stream.of("A","B","C","D","E").map(s->s.toLowerCase());Stream.of("A","B","C","D","E").map(String::toLowerCase);
前面的三行代码都完全一样&#xff0c;因为lambda映射到功能接口的匿名类。
在Kotlin中&#xff0c;这非常相似&#xff0c;但有2点&#xff1a;
it
Stream.of("A","B","C","D","E").map{it.toLowerCase()}Stream.of("A","B","C","D","E").map(String::toLowerCase)
一旦将一个函数传递给另一个函数&#xff0c;下一个逻辑步骤就是调用它。 在Java中&#xff0c;需要知道高阶函数的类型。 例如&#xff0c;要执行一个Function &#xff0c;需要调用apply()并传递具有期望类型的参数。 要执行Predicate &#xff0c;该函数称为test()等。
Function
apply()
Predicate
test()
Function<String,String>toLowerCase&#61;String::toLowerCase;toLowerCase.apply("A");Predicate<String>isLowerCase&#61;s->s.equals(s.toLowerCase());isLowerCase.test("A");
Kotlin更加一致&#xff0c;因为在KCallable接口上定义了一个名为call()函数。
KCallable
call()
funisLowerCase(string:String)&#61;string&#61;&#61;string.toLowerCase()valcallable:Boolean&#61;::isLowerCase.call("A")
但是&#xff0c; call()接受任意数量的Any?类型的参数Any? 。 调用者可以传递正确的数字和参数类型。 以下编译&#xff0c;但在运行时将失败&#xff1a;
Any?
valcallable:KCallable<String>&#61;::isLowerCase.call("A",2,Any())
为了从编译器中受益&#xff0c;可以使用KFunctionX类型&#xff0c;其中X是函数接受的参数数量。 该类型为invoke()函数提供了正确数量的参数及其类型。
KFunctionX
X
invoke()
valok:KFunction<String,Boolean>&#61;::isLowerCase.invoke("A") (1)valko:KFunction<String,Boolean>&#61;::isLowerCase.invoke("A",2,Any()) (2)
锦上添花&#xff0c; invoke()是一个运算符函数&#xff0c;该运算符什么都不是......因此以下语法也是有效的&#xff1a;
(::isLowerCase)("A")
在本练习中&#xff0c;入口点函数将调用一个函数&#xff0c;该函数依次调用7个嵌套级别。 如果将KCallable更改为KFunction &#xff0c;则顶部函数具有以下非常“有趣”的签名&#xff1a;
KFunction
funreadFile(filename:String,function:KFunction2<List<String>,KFunction2<List<String>,KFunction2<List<String>,KFunction2<List<String>,KFunction2<List<String>,KFunction2<List<Pair<String,Int>>,KFunction1<List<Pair<String,Int>>,Map<String,Int>>,Map<String,Int>>,Map<String,Int>>,Map<String,Int>>,Map<String,Int>>,Map<String,Int>>,Map<String,Int>>):Map<String,Int>
尽管我相信静态类型化有很多好处&#xff0c;但是这当然不会使代码更易于阅读。 为了解决这个问题&#xff0c;Kotlin提供了类型别名 。 以下代码段使用它们来改善情况&#xff1a;
typealiasWordFrequency&#61;Pair<String,Int>typealiasWordFrequencies&#61;List<WordFrequency>typealiasLines&#61;List<String>typealiasWords&#61;List<String>typealiasMapFunction&#61;KFunction1<WordFrequencies,Map<String,Int>>typealiasSortFunction&#61;KFunction2<WordFrequencies,MapFunction,Map<String,Int>>typealiasFrequencyFunction&#61;KFunction2<Words,SortFunction,Map<String,Int>>typealiasRemoveFunction&#61;KFunction2<Lines,FrequencyFunction,Map<String,Int>>typealiasScanFunction&#61;KFunction2<Lines,RemoveFunction,Map<String,Int>>typealiasNormalizeFunction&#61;KFunction2<Lines,ScanFunction,Map<String,Int>>typealiasReadFunction&#61;KFunction2<Lines,NormalizeFunction,Map<String,Int>>funreadFile(filename:String,function:ReadFunction)&#61;function(read(filename),::normalize)
这是最终代码的摘录&#xff1a;
funrun(filename:String)&#61;(::readFile)(filename,::filterChars) (1)funreadFile(filename:String,function:ReadFunction)&#61;function(read(filename),::normalize) (2)funfilterChars(lines:List<String>,function:NormalizeFunction):Map<String,Int>{ (3)valpattern&#61;"\\W|_".toRegex()valfiltered&#61;lines.map{it.replace(pattern," ")}returnfunction(filtered,::scan) (3)}
function
至于介绍&#xff0c;虽然类型有助于降低某些错误的可能性&#xff0c;但它们也会降低可读性。 幸运的是&#xff0c;Kotlin类型别名对此很有帮助。
call()和invoke()是使用反射调用函数时要考虑的两个选项。 他们每个人都有优点和缺点。
翻译自: https://blog.frankel.ch/exercises-programming-style/5/