mosceo
mosceo

Reputation: 1224

Call a function with arguments from a list

Is there a way to call a function with arguments from a list? The equivalent in Python is sum(*args).

// Scala

def sum(x: Int, y: Int) = x + y
val args = List(1, 4)

sum.???(args)      // equivalent to sum(1, 4)

sum(args: _*) wouldn't work here. Don't offer change the declaration of the function anyhow. I'm acquainted with a function with repeated parameters def sum(args: Int*).

Upvotes: 6

Views: 1443

Answers (2)

Jason Evans
Jason Evans

Reputation: 1237

I wouldn't recommend it for most uses since it's a bit complicated and hard to read, bypasses compile-time checks, etc., but if you know what you're doing and need to do this, you can use reflection. This should work with any arbitary parameter types. For example, here's how you might call a constructor with arguments from a list:

import scala.reflect.runtime.universe

class MyClass(
  val field1: String,
  val field2: Int,
  val field3: Double)

// Get our runtime mirror
val runtimeMirror = universe.runtimeMirror(getClass.getClassLoader)

// Get the MyClass class symbol
val classSymbol = universe.typeOf[MyClass].typeSymbol.asClass

// Get a class mirror for the MyClass class
val myClassMirror = runtimeMirror.reflectClass(classSymbol)

// Get a MyClass constructor representation
val myClassCtor = universe.typeOf[MyClass].decl(universe.termNames.CONSTRUCTOR).asMethod

// Get an invokable version of the constructor
val myClassInvokableCtor = myClassMirror.reflectConstructor(myClassCtor)

val myArgs: List[Any] = List("one", 2, 3.0)

val myInstance = myClassInvokableCtor(myArgs: _*).asInstanceOf[MyClass]

Upvotes: 0

0__
0__

Reputation: 67330

Well, you can write

sum(args(0), args(1))

But I assume you want this to work for any list length? Then you would go for fold or reduce:

args.reduce(sum)  // args must be non empty!

(0 /: args)(sum)  // aka args.foldLeft(0)(sum)

These methods assume a pair-wise reduction of the list. For example, foldLeft[B](init: B)(fun: (B, A) => B): B reduces a list of elements of type A to a single element of type B. In this example, A = B = Int. It starts with the initial value init. Since you want to sum, the sum of an empty list would be zero. It then calls the function with the current accumulator (the running sum) and each successive element of the list.

So it's like

var result = 0
result = sum(result, 1)
result = sum(result, 4)
...

The reduce method assumes that the list is non-empty and requires that the element type doesn't change (the function must map from two Ints to an Int).

Upvotes: 2

Related Questions