如何使下面的scala不可变
假设我有以下代码: BroadcastMessage以迭代器的形式获取只能遍历一次的学生组列表 呼叫如何使下面的scala不可变,scala,functional-programming,immutability,Scala,Functional Programming,Immutability,假设我有以下代码: BroadcastMessage以迭代器的形式获取只能遍历一次的学生组列表 呼叫sendMessage时,它将向小组中的所有学生发送消息 class BroadcastMessage(message:String,groups:List[Iterator[Student]]) { def sendMessage:Unit = { groups.foreach(group=>group.foreach(sendeMessage)) } priv
sendMessage
时,它将向小组中的所有学生发送消息
class BroadcastMessage(message:String,groups:List[Iterator[Student]]) {
def sendMessage:Unit = {
groups.foreach(group=>group.foreach(sendeMessage))
}
private def sendMessage(student:Student): Unit ={
EmailClient.sendMessage(student.email,message)
}
}
case class Student(id: String,email:String)
假设学生可以分成几个小组,我们不想给他发送多封电子邮件
可变解决方案是添加可变集合,将学生的id添加到集合中,并且仅在集合中存在id时发送消息
class BroadcastMessage(message:String,groups:List[Iterator[Student]]) {
// evil mutable set
private var set:scala.collection.mutable.Set[String] = Set()
def sendMessage:Unit = {
groups.foreach(group=>group.foreach(sendeMessage))
}
private def sendMessage(student:Student): Unit ={
if (set.add(student.id)) {
EmailClient.sendMessage(student.email, message)
}
}
}
如何以不可变的方式实现它?如果没有内存限制,您可以这样做:
def sendMessage:Unit = {
groups.flatten.distinct.foreach(sendMessage)
}
如果没有内存限制,可以执行以下操作:
def sendMessage:Unit = {
groups.flatten.distinct.foreach(sendMessage)
}
我认为在你的例子中,你在做两个不同的可变的东西,而你实际上只需要一个 您需要一个
private-var-set:mutable.set[Student]=mutable.set.empty[Student]
或一个private-var-set:set[Student]=set.empty[Student]
。也就是说,您需要对集合本身进行变异,或者只对类持有的集合进行引用。我个人倾向于后者,结果是:
case class Student(id: String,email:String)
class BroadcastMessage(message:String,groups:List[Iterator[Student]]) {
private var set : Set[Student] = Set.empty // <- no mutable Set, just a mutable reference to several immutable Sets
def sendMessage:Unit = {
groups.foreach(group=>group.foreach(sendMessage))
}
private def sendMessage(student:Student): Unit = {
if (!set(student)) {
set = set + student
EmailClient.sendMessage(student.email, message)
}
}
}
我想这取决于风格…好吧,我认为你在你的例子中创造了两种不同的可变事物,而你实际上只需要一种 您需要一个
private-var-set:mutable.set[Student]=mutable.set.empty[Student]
或一个private-var-set:set[Student]=set.empty[Student]
。也就是说,您需要对集合本身进行变异,或者只对类持有的集合进行引用。我个人倾向于后者,结果是:
case class Student(id: String,email:String)
class BroadcastMessage(message:String,groups:List[Iterator[Student]]) {
private var set : Set[Student] = Set.empty // <- no mutable Set, just a mutable reference to several immutable Sets
def sendMessage:Unit = {
groups.foreach(group=>group.foreach(sendMessage))
}
private def sendMessage(student:Student): Unit = {
if (!set(student)) {
set = set + student
EmailClient.sendMessage(student.email, message)
}
}
}
我想这取决于当时的风格…我设法做到了这一点。我想我失去了一些可读性,但它是可变的:
class BroadcastMessage(message: String, groups: List[Iterator[Student]]) {
def sendMessage(): Unit = {
groups.foldLeft[Set[String]](Set.empty)(sendMessage)
}
private def sendMessage(sent: Set[String], group: Iterator[Student]):Set[String] = {
group.foldLeft[Set[String]](sent)(sendMessage)
}
private def sendMessage(sent: Set[String], student: Student): Set[String] = {
if (!sent.contains(student.id)) {
EmailClient.sendMessage(student.email, message)
return sent + student.id
}
sent
}
}
我设法做到了这一点。我想我失去了一些可读性,但它是可变的:
class BroadcastMessage(message: String, groups: List[Iterator[Student]]) {
def sendMessage(): Unit = {
groups.foldLeft[Set[String]](Set.empty)(sendMessage)
}
private def sendMessage(sent: Set[String], group: Iterator[Student]):Set[String] = {
group.foldLeft[Set[String]](sent)(sendMessage)
}
private def sendMessage(sent: Set[String], student: Student): Set[String] = {
if (!sent.contains(student.id)) {
EmailClient.sendMessage(student.email, message)
return sent + student.id
}
sent
}
}
您可以使用一个班轮:
def发送消息:单位=
groups.reduce(++).toStream.distinct.foreach(sendMessage)
学习用扩展版:
val学生:迭代器[Student]=组。减少(++)
val sstudens:Stream[学生]=students.toStream
val dStudents:Stream[Student]=sstudens.distinct
def sendMessage:Unit=sStudents.foreach(sendMessage)
您可以使用一行程序:
def发送消息:单位=
groups.reduce(++).toStream.distinct.foreach(sendMessage)
学习用扩展版:
val学生:迭代器[Student]=组。减少(++)
val sstudens:Stream[学生]=students.toStream
val dStudents:Stream[Student]=sstudens.distinct
def sendMessage:Unit=sStudents.foreach(sendMessage)
看起来您要查找的内容在嵌套集合中都是唯一的Student
s
一个非常简单的方法是将集合展平并将其转换为集合
;以下是Int
s的示例:
scala> val groups = List(Iterator(1,2,3), Iterator(3,4,5))
groups: List[Iterator[Int]] = List(non-empty iterator, non-empty iterator)
scala> val unique: Set[Int] = groups.flatten.toSet
unique: Set[Int] = Set(5, 1, 2, 3, 4)
这里的一个问题是toSet
方法实际上复制了您的列表。为了避免这种情况,您可以使用以下小技巧(您可以阅读更多关于collection.breakOut
和CanBuildFrom
):
然而,这里的易变性的来源是一个
迭代器的使用,它无论如何都会被使用,在那里会发生变异和破坏。看起来你所寻找的是嵌套集合中唯一的学生s
一个非常简单的方法是将集合展平并将其转换为集合
;以下是Int
s的示例:
scala> val groups = List(Iterator(1,2,3), Iterator(3,4,5))
groups: List[Iterator[Int]] = List(non-empty iterator, non-empty iterator)
scala> val unique: Set[Int] = groups.flatten.toSet
unique: Set[Int] = Set(5, 1, 2, 3, 4)
这里的一个问题是toSet
方法实际上复制了您的列表。为了避免这种情况,您可以使用以下小技巧(您可以阅读更多关于collection.breakOut
和CanBuildFrom
):
然而,这里的易变性的来源是使用一个迭代器,它无论如何都会被使用,会发生变异和破坏。我认为OP希望这样,如果再次调用该方法,相同的学生
不会被发送新消息。在本例中,我没有内存限制,但我的真实代码中有它。学生迭代器实际上是一个文件流。你可以使用流,它也有“distinct”操作,这样你就不会有任何内存问题。我认为OP希望这样,如果再次调用该方法,相同的学生
不会被发送新消息。在本例中,我没有内存限制,但我的真实代码中有它。学生迭代器实际上是一个文件流。你可以使用流,它还有“独特”操作,这样你就不会有任何内存问题。你觉得我的解决方案怎么样?但我看到在你的秒解决方案中,你在每个项目上遍历两次。。。这是我做不到的。(我知道在这个例子中它看起来不错,但在我的真实代码中,学生迭代器实际上是文件读取器)。我认为它工作正常,但我认为您需要集合,因为如果您的方法被多次调用,您将不希望再次通知学生。如果是这种情况,那么您的解决方案就是将跟踪这种情况的责任委托给方法的调用方。如果你同意,那就太好了。:)我不认为我对迭代器进行了两次迭代……我确实对内部集合进行了两次检查,但每个迭代器只扫描一次,student您有一个很好的观点。我希望我能让事情变得更无状态,尽管(没有var
)。很好。你觉得我的解决方案怎么样?但我看到在你的秒解决方案中,你在每个项目上遍历两次。。。这是我做不到的。(我知道在这个例子中,它看起来还可以,但在我的实际代码中,学生迭代器实际上是文件读取器)。我认为它可以工作,但我认为您需要集合,因为如果您的方法被多次调用,您将