我正在向一位 friend (他大部分时间使用 Java)做广告,他问我一个挑战:在 Scala 中编写数组 {1, 2, 4, 8, 16} 的方法是什么。
我不太了解函数式编程,但我真的很喜欢 Scala。然而,这是一个由(n*(n-1))形成的迭代数组。 ,但是如何跟踪上一步呢?有没有一种方法可以在 Scala 中轻松完成,或者我是否必须编写多行代码才能实现这一目标?
请您参考如下方法:
Array.iterate(1, 5)(2 * _)
或者
Array.iterate(1, 5)(n => 2 * n)
按照评论中的要求详细说明这一点。不知道你想让我详细说明什么,希望你能找到你需要的。
这是 上的函数 iterate(start,len)(f)对象 数组 ( scaladoc )。那将是 静态 在 java 。
重点是填充一个数组
len元素,从第一个值
start并且总是通过将前一个元素传递给函数 f 来计算下一个元素。
一个基本的实现是
import scala.reflect.ClassTag
def iterate[A: ClassTag](start: A, len: Int)(f: A => A): Array[A] = {
val result = new Array[A](len)
if (len > 0) {
var current = start
result(0) = current
for (i <- 1 until len) {
current = f(current)
result(i) = current
}
}
result
}
(实际实现中,可以找到 here 差别不大。主要是因为相同的代码用于不同的数据结构,例如
List.iterate )
除此之外,实现非常简单。语法可能需要一些解释:
def iterate[A](...) : Array[A]使其成为通用方法,可用于任何类型 A。那将是
public <A> A[] iterate(...)在 java 。
ClassTag只是一个技术问题,在 Scala 和 Java 中,您通常无法创建泛型类型的数组(java
new E[]),而
: ClassTag要求编译器添加一些魔法,这与在方法声明中添加并在调用站点传递
class<A> clazz 非常相似。 java中的参数,然后可用于通过反射创建数组。如果您执行例如 List.iterate 而不是 Array.iterate,则不需要。
也许更令人惊讶的是,两个参数列表,一个带有 start 和 len,然后在一个单独的括号中,一个带有 f。 Scala 允许一个方法有多个参数列表。这里的原因是 scala 进行类型推断的特殊方式:查看第一个参数列表,它将根据 start 的类型确定什么是 A。只有在之后,它才会查看第二个列表,然后才知道 A 是什么类型。否则,它需要被告知,所以如果只有一个参数列表, def iterate[A: ClassTag](start: A, len: Int, f: A => A),
那么电话应该是
Array.iterate(1, 5, n : Int => 2 * n) Array.iterate[Int](1, 5, n => 2 * n) Array.iterate(1, 5, 2 * (_: int)) Array.iterate[Int](1, 5, 2 * _) 制作
Int明确的一种或另一种方式。所以在scala 中将函数参数放在一个单独的参数列表中是很常见的。该类型可能比'Int'要长得多。
A => A只是类型
Function1[A,A 的语法糖]。显然,函数式语言具有作为(第一类)值的函数,类型化函数式语言具有函数类型。
通话中,
iterate(1, 5)(n => 2 * n) ,
n => 2 * n是函数的值。更完整的声明是
{n: Int => 2 * n} ,但由于上述原因,可以免除 Int。 Scala 语法相当灵活,您也可以省去圆括号或方括号。所以它可能是
iterate(1, 5){n => 2 * n} . curl 允许包含多个指令的完整块,此处不需要。
至于不变性,Array 基本上是可变的,除了在某个时刻更改数组之外,没有办法将值放入数组中。我的实现(以及库中的实现)也使用了一个可变的
var (
current ) 和副作用
for ,这不是绝对必要的,(尾)递归实现的编写时间只会稍微长一点,并且同样有效。但是一个可变的本地并没有太大的伤害,无论如何我们已经在处理一个可变数组。




