【问题标题】:Scala; Can you define many parameters as one斯卡拉;你能把多个参数定义为一个吗
【发布时间】:2012-07-15 20:19:28
【问题描述】:

你能定义一组变量供以后使用吗?

这里有一些伪代码突出了我的意图:

def coordinates = x1, y1, x2, y2

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

这是一个包含重复代码的工作示例。

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

【问题讨论】:

  • 不确定您的意图是什么,也许 varags 可以提供帮助:stackoverflow.com/questions/4176440/syntax-sugar
  • Varags 不起作用。 Varags 必须是最后一个参数,因此不通用。
  • 你看过 Scala 宏吗? scalamacros.org 不过,它们是一个实验性功能。
  • 实际上没有,因为我在 2.9.2 分支上。但我会在以后研究。
  • 对这样的问题使用宏似乎有点过分(而且可能是让所有读过你代码的人都讨厌你的好方法)。

标签: scala functional-programming


【解决方案1】:

是的,你可以,虽然语法可以说是非常笨拙,并且有一些限制,一开始可能看起来有点武断。诀窍是将方法转换为函数(称为“eta 扩展”),然后使用该函数的 tupled 方法获取可以应用于元组的内容。

假设你有这样一个类:

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

还有一个实例:

val foo = new Foo

还有一些你想在Foo的方法上使用的数据:

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

您不能只写foo.f(names)foo.g(nums),因为类型不对齐——参数列表和元组在Scala 中是不同的东西。但是你可以这样写:

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

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

在方法将其转换为函数之后添加下划线(我认为这是 Scala 语法中最令人困惑的小怪癖),tupled 将其从具有两个(或三个)参数的函数转换为具有单个元组参数。

您可以通过定义以下辅助函数来稍微清理一下代码,例如:

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

不过,我不确定这是否更好。

最后,您不能(方便地)在 varargs 方法上使用这种方法——例如,您不能编写以下代码:

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

甚至只是:

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

这是在 Scala 中避免使用可变参数的另一个原因。

【讨论】:

  • 感谢非常足智多谋的帖子。这种方法可以在使用元组中的前 4 个参数然后正常使用的方法时使用吗?像 m(p1,p2,p3,p4,p5) 写成 m(Tuple4, p5)
  • 只要他们对Any 类型感到满意,您就可以在可变参数上使用它:printf("%d %d %d %s", (1,2,3,"fish").productIterator.toSeq: _*)
【解决方案2】:

看起来你需要一个元组:

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

或者可能是一个成熟的对象?

【讨论】:

  • 我不认为tuple 是我所追求的吗?或者你可以自动扩展它们吗?否则我必须做方法(坐标._1,坐标._2,坐标._3,坐标._4),这不是我想要的
  • @Farmor:如果您可以控制要以这种方式调用的方法,则可以使用以下命令轻松提取元组:val (x1, y1, x2, y2) = coordinates
  • 不,我没有,因为它是 Androids API,它在 Canvas 上公开 drawLine。我主要感兴趣的是 Scala 是否能够定义一组参数,然后以概念查找和替换的方式使用它们。
【解决方案3】:

现在,这可能很明显,但如果它只在少数情况下让您烦恼,您可以随时改进:

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)
  }
}

另一种可能性,如果你足够疯狂的话。 (可能这样的增强功能已经在 Scalaz 或 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))

【讨论】:

    猜你喜欢
    • 2018-01-13
    • 2013-11-25
    • 2015-09-03
    • 1970-01-01
    • 2016-06-16
    • 2021-08-06
    • 2017-12-21
    • 1970-01-01
    相关资源
    最近更新 更多