简体   繁体   中英

Scala; Can you define many parameters as one

Can you define a set of variables for later use?

Here are some pseudo code highlighting my intent:

def coordinates = x1, y1, x2, y2

log("Drawing from (%4.1f, %4.1f) to (%4.1f, %4.1f)".format(coordinates))
canvas.drawLine(coordinates, linePaint)

Here is a working example that contains duplicated code.

log("Drawing from (%4.1f, %4.1f) to (%4.1f, %4.1f)".format(x1, y1, x2, y2))
canvas.drawLine(x1, y1, x2, y2, linePaint)

Yes, you can, although the syntax is arguably horribly clunky, and there are some limitations that may seem a little arbitrary at first. The trick is to convert the method to a function (called "eta expansion"), and then to use that function's tupled method to get something you can apply to a tuple.

Suppose you have a class like this:

class Foo {
  def f(a: String, b: String) = "%s, %s".format(b, a)
  def g(x: Int, y: Int, z: Int) = x + y * z
}

And an instance:

val foo = new Foo

And some data you'd like to use Foo 's methods on:

val names = ("John", "Doe")
val nums = (42, 3, 37)

You can't just write foo.f(names) or foo.g(nums) , because the types don't line up—argument lists and tuples are different things in Scala. But you can write the following:

scala> (foo.f _).tupled(names)
res0: String = Doe, John

scala> (foo.g _).tupled(nums)
res1: Int = 153

Sticking the underscore after the method turns it into a function (this is in my opinion the most confusing little quirk of Scala's syntax), and tupled converts it from a function with two (or three) arguments to a function with a single tuple argument.

You could clean the code up a little by defining the following helper functions, for example:

scala> val myF = (foo.f _).tupled
myF: ((String, String)) => String = <function1>

scala> val myG = (foo.g _).tupled
myG: ((Int, Int, Int)) => Int = <function1>

scala> myF(names)
res2: String = Doe, John

scala> myG(nums)
res3: Int = 153

I'm not sure that's much better, though.

Lastly, you can't (conveniently) use this approach on a varargs method—you can't for example write the following:

val coordsTupleToString = ("(%4.1f, %4.1f) to (%4.1f, %4.1f)".format _).tupled

Or even just:

val coordsToString = "(%4.1f, %4.1f) to (%4.1f, %4.1f)".format _

Which is yet another reason to avoid varargs in Scala.

Looks like you need a tuple:

val coordinates = (x1, y1, x2, y2)

or maybe a full-blown object?

Now, this may be obvious, but if it annoys you in only a few cases, you can always enhance:

implicit def enhancedCanvas(canvas: Canvas) = new {
  // using bad and slow syntax. please change this in Scala 2.10.
  def drawLineC(coordinates: (Float, Float, Float, Float), paint: Paint) = {
    val (x1, y1, x2, y2) = coordinates
    canvas.drawLine(x1, y1, x2, y2, paint)
  }
}

Another possibility, if you're crazy enough. (Might be that an enhancement like this is already in Scalaz or Shapeless.)

implicit def enhTuple4[A,B,C,D](t: Tuple4[A,B,C,D]) = new {
  def |<[E] (f: (A, B, C, D) => E) = f(t._1, t._2, t._3, t._4)
}

// to be used as
val coordinates = (x1, y1, x2, y2)
coordinates |< (canvas.drawLine(_, _, _, _, linePaint))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM