【问题标题】:In Scala, how do I create a new function by setting one argument in another function to the value of a variable?在 Scala 中,如何通过将另一个函数中的一个参数设置为变量的值来创建一个新函数?
【发布时间】:2014-06-07 17:32:32
【问题描述】:

这是我做我想做的事的失败尝试:

var currentNum = 0
def adder = (x:Int, y:Int) => x+y
def getNumAdder = {x: Int =>
  val currentNumSnapshot = currentNum
  adder(x, currentNumSnapshot)
}
currentNum = 5
val addFive = getNumAdder
currentNum = 9
val addNine = getNumAdder

println("10 plus 5 is "+addFive(10))
println("10 plus 9 is "+addNine(10))

":paste" 在 Scala REPL 中,你会得到这个:

10 plus 5 is 19
10 plus 9 is 19

我希望能够调用 getNumAdder 并获得一个将当前值的快照添加到输入数字的函数。我可以更改什么以获得所需的行为?

更新:

根据促进引用透明度的警告,我相信这段代码,一个简单的 Scala 柯里化示例,也可以满足我的需要。

var currentNum = 0
def adder(x:Int) = (y:Int) => x+y
def getNumAdder(input: Int) = {
  adder(input)
}
currentNum = 5
val addFive = getNumAdder(currentNum)

【问题讨论】:

  • 我认为编写这样的代码不是一个好习惯。它取决于上下文并且不透明。

标签: function scala closures


【解决方案1】:
def getNumAdder = {
  val currentNumSnapshot = currentNum
  adder((_: Int), currentNumSnapshot) // or (x: Int) => adder(x, currentNumSnapshot)
}

请注意,它必须是def,而不是val,以便每次调用getNumAdder 时都会评估currentNumSnapshot

注意

{x: Int =>
  val currentNumSnapshot = currentNum
  adder(x, currentNumSnapshot)
}

完全不同
{
  val currentNumSnapshot = currentNum
  (x: Int) => adder(x, currentNumSnapshot)
}

在我的版本中,当评估getNumAdder 时,我首先保存currentNum 的当前值,然后返回使用此快照的函数。在您的版本中,整个块是返回的函数,因此 val currentNumSnapshot = currentNum 行仅在调用函数时执行(例如 addFive(10)),因此显然当时使用了 currentNum 的值。

正如 Goral 所说,在实际代码中做这样的事情通常是个坏主意,我假设这只是为了理解。

【讨论】:

  • 不幸的是,这似乎不起作用,但也许我在 REPL 中的不精确测试有问题。它不喜欢 _ ... 说输入参数没有类型...所以我改为传入 x:Int ,这很有效。但主要问题是,我在更改 var 之前和之后创建的函数都添加了我设置的最后一个数字。
  • 用代码更新了我的问题,您可以轻松地将其粘贴到 REPL 中以查看发生了什么。不管怎样,谢谢。
  • 谢谢,很高兴看到这最终是如何完成的!这实际上是对实际代码的简化,可能。现有组件的单参数方法存储并稍后执行函数,我需要该函数每次根据初始操作表现不同。 “currentNum”代替了存储初始动作的变量。我会修改存储方法以接受另一个参数,但此时破坏现有代码很危险。
【解决方案2】:

我不知道这个是否满足你,但它正在工作:

  var currentNum = 0                              //> currentNum  : Int = 0
  def adder = (x: Int, y: Int) => x + y           //> adder: => (Int, Int) => Int
  val getNumAdder = { x: Int =>
    val currentNumSnapshot = currentNum
    println(currentNumSnapshot)
    adder(x, currentNumSnapshot)                  //> getNumAdder  : Int => Int = <function1>
  }

  def curriedAdder = adder.curried                //> curriedAdder: => Int => (Int => Int)
  currentNum = 5
  val addFive = curriedAdder(currentNum)          //> addFive  : Int => Int = <function1>
  currentNum = 9
  val addNine = curriedAdder(currentNum)          //> addNine  : Int => Int = <function1>

  println("10 plus 5 is " + addFive(10))          //> 10 plus 5 is 15
  println("10 plus 9 is "+addNine(10))            //> 10 plus 9 is 19

【讨论】:

  • 您甚至不再使用getNumAdder,而只是明确传递currentNum 的值——这与所询问的内容有什么关系?
【解决方案3】:

问题是您在调用函数而不是创建函数时捕获了currentNum 的值。所以你必须把它改成:

var currentNum = 0
def adder = (x:Int, y:Int) => x + y
def getNumAdder = {
  val currentNumSnapshot = currentNum
  x: Int => adder(x, currentNumSnapshot)
}
currentNum = 5
val addFive = getNumAdder
currentNum = 9
val addNine = getNumAdder

这将按预期工作:

scala> println("10 plus 5 is "+addFive(10))
10 plus 5 is 15

scala> println("10 plus 9 is "+addNine(10))
10 plus 9 is 19

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 2014-11-27
    • 2012-10-26
    • 2016-10-18
    • 2017-05-18
    • 2011-05-21
    • 2015-04-12
    相关资源
    最近更新 更多