【问题标题】:How to idiomatically add element to immutable set and get if success?如何惯用地将元素添加到不可变集合并获得成功?
【发布时间】:2019-05-15 23:34:22
【问题描述】:

我有一个不可变的集合来跟踪一组订阅者。这是添加 Subs 并返回 Boolean 以指示订阅是否已实际处理的方法:

var subs = Set.empty[Sub]
def addSub(addr: String, sub: Sub): Boolean = {
  if(subs.contains(sub)) {
    false
  }
  else {
    subscribe(addr)
    subs += sub
    true
  }
}

但是,这段代码非常“类似于 Java”。我试图让它更 Scala/Functional 但找不到方法,因为 Scala 中的赋值不会返回值,所以 subs += sub 必须在它自己的代码行中,让它看起来像 Java。我应该如何改进它?

我知道mutable.Set.add() 实际上返回布尔值。但是如何使用immutable.Set 做到这一点?

【问题讨论】:

  • 您能给代码提供更多上下文吗?您声称您希望在功能上执行此操作,但您的代码示例取决于使用可变状态。没有纯粹的函数式方式来改变状态,因为一旦你这样做了,它就不再是纯粹的了。

标签: scala functional-programming


【解决方案1】:

Scala/Functional 方法是尽可能使用本地范围,以保持一切不可变并避免副作用。

def addSub(addr :String, sub :Sub, subs :Set[Sub]): (Boolean, Set[Sub]) = {
  if(!subs(sub)) subscribe(addr)
  (!subs(sub), subs + sub)
}

用法:

val (subAdded, newSubs) = addSub(address, subscriber, oldSubs)

【讨论】:

  • 从命令式思维到函数式思维的转变通常是困难的(甚至是痛苦的)。如果您有一个维护状态的Set[Sub],那么根据定义,每次修改都是副作用。检查subs 的代码可以将其作为传递的参数接收。修改subs 的代码可以返回一个新的/修改后的副本。这样,子集始终是本地的且不可变的。
【解决方案2】:

在你的场景中使用 varval。然后,您可以执行以下操作来检查该属性,如果不存在则添加它。

var set = Set.empty[Sub]
set
  .find(_ == newVal) // checks for the property and converts to an Option[Sub] presence
  .fold[Boolean] {
    set = set + newVal // property is not in set, add and return true
    true
  }(_ => false) // property is set, return false

【讨论】:

  • 有什么办法可以避免使用真/假字面量?因为它依赖于包含/查找结果
  • 您可以将fold 的结果设置为Option[Unit],并在折叠后调用exists(),而不是使用文字。但我个人认为这是语法糖而不是仅仅返回文字。
  • OP 要求提供功能解决方案,但这使用了一个可变变量。
猜你喜欢
  • 2017-01-03
  • 1970-01-01
  • 2019-09-21
  • 1970-01-01
  • 2014-01-20
  • 1970-01-01
  • 1970-01-01
  • 2015-08-05
  • 2021-08-05
相关资源
最近更新 更多