Scala 在函数中第二次使用变量不返回相同的值?
我开始学习Scala,并编写了代码。我有个问题,为什么val是常数?当我第二次将它传递给同一个函数时,是否返回其他值?如何在scala中编写纯函数 如果计算正确,还有什么评论吗Scala 在函数中第二次使用变量不返回相同的值?,scala,iterator,immutability,Scala,Iterator,Immutability,我开始学习Scala,并编写了代码。我有个问题,为什么val是常数?当我第二次将它传递给同一个函数时,是否返回其他值?如何在scala中编写纯函数 如果计算正确,还有什么评论吗 import java.io.FileNotFoundException import java.io.IOException import scala.io.BufferedSource import scala.io.Source.fromFile object Main{ def main(args: A
import java.io.FileNotFoundException
import java.io.IOException
import scala.io.BufferedSource
import scala.io.Source.fromFile
object Main{
def main(args: Array[String]): Unit = {
val fileName: String = if(args.length == 1) args(0) else ""
try {
val file = fromFile(fileName)
/* In file tekst.txt is 4 lines */
println(s"In file $fileName is ${countLines(file)} lines")
/* In file tekst.txt is 0 lines */
println(s"In file $fileName is ${countLines(file)} lines")
file.close
}
catch{
case e: FileNotFoundException => println(s"File $fileName not found")
case _: Throwable => println("Other error")
}
}
def countLines(file: BufferedSource): Long = {
file.getLines.count(_ => true)
}
}
val
表示不能为其分配新值。如果这是不可变的东西—数字、不可变集合、元组或其他不可变事物的case类—那么您的值在其生命周期内不会改变—如果这是函数中的val
,当您为其赋值时,它将保持不变,直到您离开该函数为止。如果这是类中的值,则在对该类的所有调用之间它将保持不变。如果这是一个对象,它将在整个程序生命周期内保持不变
但是,如果您谈论的对象本身是可变的,那么唯一不变的部分就是对对象的引用。如果val为mutable.MutableList
,则可以将其与另一个mutable.MutableList
交换,但可以修改列表的内容。在这里:
val file = fromFile(fileName)
/* In file tekst.txt is 4 lines */
println(s"In file $fileName is ${countLines(file)} lines")
/* In file tekst.txt is 0 lines */
println(s"In file $fileName is ${countLines(file)} lines")
file.close
文件
是对缓冲源
的不可变引用。您不能将其替换为另一个BufferedSource
-但该类具有内部状态,它计算已读取文件中的行数,因此第一次对其进行操作时,您将收到文件中的总行数,然后(因为已读取文件)0
如果你想让代码更纯粹,你应该包含易变性,这样用户就看不到它
def countFileLines(fileName: String): Either[String, Long] = try {
val file = fromFile(fileName)
try {
Right(file.getLines.count(_ => true))
} finally {
file.close()
}
} catch {
case e: FileNotFoundException => Left(s"File $fileName not found")
case _: Throwable => Left("Other error")
}
println(s"In file $fileName is ${countLines(fileName)} lines")
println(s"In file $fileName is ${countLines(fileName)} lines")
尽管如此,您仍然有副作用,因此理想情况下,它应该是使用IO monad编写的,但现在请记住,您应该以引用透明性为目标-如果您可以使用
val counted=countLines(file)
中的值替换对countLines(file)
的每个调用,则它将是RT。正如您所检查的,它不是。所以,用一个如果被调用两次也不会改变行为的东西来替换它。一种方法是调用整个计算两次,其间不保留任何全局状态(例如,BufferedSource
中的内部计数器)。IO单子使这变得更容易,因此,一旦您对语法本身感到满意,就使用它们(以避免一次学习太多东西)。val
意味着您无法为它分配新的值。如果这是不可变的东西—数字、不可变集合、元组或其他不可变事物的case类—那么您的值在其生命周期内不会改变—如果这是函数中的val
,当您为其赋值时,它将保持不变,直到您离开该函数为止。如果这是类中的值,则在对该类的所有调用之间它将保持不变。如果这是一个对象,它将在整个程序生命周期内保持不变
但是,如果您谈论的对象本身是可变的,那么唯一不变的部分就是对对象的引用。如果val为mutable.MutableList
,则可以将其与另一个mutable.MutableList
交换,但可以修改列表的内容。在这里:
val file = fromFile(fileName)
/* In file tekst.txt is 4 lines */
println(s"In file $fileName is ${countLines(file)} lines")
/* In file tekst.txt is 0 lines */
println(s"In file $fileName is ${countLines(file)} lines")
file.close
文件
是对缓冲源
的不可变引用。您不能将其替换为另一个BufferedSource
-但该类具有内部状态,它计算已读取文件中的行数,因此第一次对其进行操作时,您将收到文件中的总行数,然后(因为已读取文件)0
如果你想让代码更纯粹,你应该包含易变性,这样用户就看不到它
def countFileLines(fileName: String): Either[String, Long] = try {
val file = fromFile(fileName)
try {
Right(file.getLines.count(_ => true))
} finally {
file.close()
}
} catch {
case e: FileNotFoundException => Left(s"File $fileName not found")
case _: Throwable => Left("Other error")
}
println(s"In file $fileName is ${countLines(fileName)} lines")
println(s"In file $fileName is ${countLines(fileName)} lines")
尽管如此,您仍然有副作用,因此理想情况下,它应该是使用IO monad编写的,但现在请记住,您应该以引用透明性为目标-如果您可以使用
val counted=countLines(file)
中的值替换对countLines(file)
的每个调用,则它将是RT。正如您所检查的,它不是。所以,用一个如果被调用两次也不会改变行为的东西来替换它。一种方法是调用整个计算两次,其间不保留任何全局状态(例如,BufferedSource
中的内部计数器)。IO monad使这变得更容易,因此,一旦您对语法本身感到满意,就使用它们(以避免一次学习太多东西)。file.getLines
返回迭代器[String]
,这意味着我们只能对其进行一次迭代,例如,请考虑
val it = Iterator("a", "b", "c")
it.count(_ => true)
// val res0: Int = 3
it.count(_ => true)
// val res1: Int = 0
查看count的实现
def count(p: A => Boolean): Int = {
var res = 0
val it = iterator
while (it.hasNext) if (p(it.next())) res += 1
res
}
注意调用it.next()
。这个调用将提升迭代器的状态,如果发生这种情况,我们将无法返回到以前的状态
或者,您可以尝试长度
而不是计数
val it = Iterator("a", "b", "c")
it.length
// val res0: Int = 3
it.length
// val res0: Int = 3
查看length
的定义,它只代表size
def size: Int = {
if (knownSize >= 0) knownSize
else {
val it = iterator
var len = 0
while (it.hasNext) { len += 1; it.next() }
len
}
}
注意警卫
if (knownSize >= 0) knownSize
有些集合知道它们的大小,而不必通过迭代来计算。比如说,
Array(1,2,3).knownSize // 3: I know my size in advance
List(1,2,3).knownSize // -1: I do not know my size in advance so I have to traverse the whole collection to find it
因此,如果迭代器的底层具体集合
知道它的大小,那么对length
的调用将缩短CUIRCIT并它。next()
将永远不会执行,这意味着迭代器将不会被使用。这是迭代器
工厂使用的默认具体集合的情况,即数组
val it = Iterator("a", "b", "c")
it.getClass.getSimpleName
// res6: Class[_ <: Iterator[String]] = class scala.collection.ArrayOps$ArrayIterator
关于val
ue定义和不变性的最后一点。考虑
object Foo { var x = 42 } // object contains mutable state
val foo = Foo // value definition
foo.x
// val res0: Int = 42
Foo.x = -11 // mutation happening here
foo.x
// val res1: Int = -11
这里的标识符foo
是对可变对象的不可变引用。文件。getLines
返回迭代器[String]
,这意味着我们只能对其进行一次迭代,例如,考虑
val it = Iterator("a", "b", "c")
it.count(_ => true)
// val res0: Int = 3
it.count(_ => true)
// val res1: Int = 0
查看count的实现
def count(p: A => Boolean): Int = {
var res = 0
val it = iterator
while (it.hasNext) if (p(it.next())) res += 1
res
}
注意调用it.next()
。这个调用将提升迭代器的状态,如果发生这种情况,我们将无法返回到以前的状态
作为替代方案,您可以尝试