Functional way of doing a loop of operations on an array

177 views Asked by At

I currently have a Java program which does something like the following:

  int nvars = 10;
  long vars[] = new long[nvars];
  for(int i = 0; i < nvars; i++) {
      vars[i] = someFunction(i);
      anotherFunction(vars[i]);
  }

I am converting it into Scala code and have:

val nvars: Int = 10
val vars: Array[Long] = new Array[Long](nvars)

for ( i <- 0 to nvars-1 )
    vars(i) = someFunction(i)
    anotherFunction(vars(i))
}

Any advice on how to make this (more) functional?

3

There are 3 answers

3
Shadowlands On BEST ANSWER

There are lots of useful constructor methods in the Array companion object, for example, for your situation here, you can use tabulate:

val nvars: Int = 10
val vars = Array.tabulate(nvars){ someFunction } // calls someFunction on the values 0 to nvars-1, and uses this to construct the array
vars foreach (anotherFunction) // calls anotherFunction on each entry in the array

If anotherFunction returns a result rather than just being a "side-effect" function, you can instead capture this with a call to map:

val vars2 = vars map (anotherFunction) // vars2 is a new array with the results computed from applying anotherFunction to each element of vars, which is left unchanged.
0
elm On

Consider iterating over a range of Long values,

val vars = (1L to 10L).map(someFunction).map(anotherFunction)

This applies someFunction onto each value from the range, and then each intermediate result from the first map onto anotherFunction. Assumed it is that each function takes and delivers a values of type Long.

For converting vars onto a desired collection such as Array or List consider,

vars.toArray
vars.toList

Use view to apply both functions at once onto each value in the range, hence not having to create an intermediate collection from mapping someFunction,

(1L to 10L).view.map(someFunction).map(anotherFunction)
0
Brian On

Use map. A call to map on an Array will return a new collection. So call map twice for each function you want to apply to all elements of the Array.

Some simple functions for demonstration:

scala> def addOne(l: Long) = l + 1
addOne: (l: Long)Long

scala> def addTwo(l: Long) = l + 2
addTwo: (l: Long)L

map the array vars using the functions defined.

scala> val vars = Array[Long](1,2,3,4,5,6,7,8,9,10)
vars: Array[Long] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> vars.map(addOne(_))
res0: Array[Long] = Array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11)

scala> vars.map(addOne(_)).map(addTwo(_))
res1: Array[Long] = Array(4, 5, 6, 7, 8, 9, 10, 11, 12, 13)

Another approach that is "more functional" and probably a good exercise is to use a recursive function that takes a function as a parameter and applies the function passed in to each element of the List.

scala> def fun[A](as: List[A], f: A => A): List[A] = as match {
     | case List() => List()
     | case h::t => f(h) :: fun(t, f)
     | }
fun: [A](as: List[A], f: A => A)List[A]