Ok, I want to generate temp file names. So, I created a class with var tempFileName and fileNo such that it creates files like
BSirCN_0.txt
BSirCN_1.txt
BSirCN_2.txt
But, to do this I have to keep count and the way I am going it is calling next() function of the class which returns the filename in sequence (should return BSirCN_4 in the above case. Now this goes against FP as I am modifying the state i.e. the count of names in the Object. How do I do it in a functional way. One way I can think of is keeping count where the function is called and just concatenate. Any other ways?
Just return a new object:
case class FileGenerator(tempFileName: String, fileNo: Long = 0) {
lazy val currentFileName = tempFileName + "_" + fileNo
lazy val next = FileGenerator(tempFileName, fileNo + 1)
}
You can then do:
val generator = FileGenerator("BSirCN")
val first = generator.currentFileName
val next = generator.next.currentFileName
You can avoid the mutations using an Iterator (or any other kind of infinite & lazy collection).
final class TempFileNamesGenerator(prefix: String) {
private[this] val generator =
Iterator
.from(start = 0)
.map(i => s"${prefix}_${i}.txt")
def next(): String =
generator.next()
}
val generator = new TempFileNamesGenerator(prefix = "BSirCN")
generator.next() // BSirCN_0.txt
generator.next() // BSirCN_1.txt
generator.next() // BSirCN_2.txt
A similar solution to one proposed by #Luis but using streams:
def namesStream(prefix: String, suffix: String): Stream[String] = Stream.from(0).map(n => s"$prefix$n$suffix")
Then use it like this:
val stream = namesStream("BSirCN_", ".txt")
stream.take(5) // BSirCN_1.txt, BSirCN_2.txt, BSirCN_3.txt, BSirCN_4.txt, BSirCN_5.txt
// or
stream.drop(10).take(2) // BSirCN_11.txt, BSirCN_12.txt
Related
I start learning Scala, and i wrote that code. And I have question, why val which is constant? When i pass it second time to the same function return other value? How write pure function in scala?
Or any comment if that counting is right?
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 means that you cannot assign new value to it. If this is something immutable - a number, immutable collection, tuple or case class of other immutable things - then your value will not change over its lifetime - if this is val inside a function, when you assign value to it, it will stay the same until you leave that function. If this is value in class, it will stay the same between all calls to this class. If this is object it will stay the same over whole program life.
But, if you are talking about object which are mutable on their own, then the only immutable part is the reference to object. If you have a val of mutable.MutableList, then you can swap it with another mutable.MutableList, but you can modify the content of the list. Here:
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
file is immutable reference to BufferedSource. You cannot replace it with another BufferedSource - but this class has internal state, it counts how many lines from file it already read, so the first time you operate on it you receive total number of lines in file, and then (since file is already read) 0.
If you wanted that code to be purer, you should contain mutability so that it won't be observable to the user e.g.
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")
Still, you are having side effects there, so ideally it should be something written using IO monad, but for now remember that you should aim for referential transparency - if you could replace each call to countLines(file) with a value from val counted = countLines(file) it would be RT. As you checked, it isn't. So replace it with something that wouldn't change behavior if it was called twice. A way to do it is to call whole computations twice without any global state preserved between them (e.g. internal counter in BufferedSource). IO monads make that easier, so go after them once you feel comfortable with syntax itself (to avoid learning too many things at once).
file.getLines returns Iterator[String] and iterator is consumable meaning we can iterate over it only once, for example, consider
val it = Iterator("a", "b", "c")
it.count(_ => true)
// val res0: Int = 3
it.count(_ => true)
// val res1: Int = 0
Looking at the implementation of count
def count(p: A => Boolean): Int = {
var res = 0
val it = iterator
while (it.hasNext) if (p(it.next())) res += 1
res
}
notice the call to it.next(). This call advances the state of the iterator and if it happens then we cannot go back to previous state.
As an alternative you could try length instead of count
val it = Iterator("a", "b", "c")
it.length
// val res0: Int = 3
it.length
// val res0: Int = 3
Looking at the definition of length which just delegates to size
def size: Int = {
if (knownSize >= 0) knownSize
else {
val it = iterator
var len = 0
while (it.hasNext) { len += 1; it.next() }
len
}
}
notice the guard
if (knownSize >= 0) knownSize
Some collections know their size without having to compute it by iterating over them. For example,
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
So if the underlying concrete collection of the Iterator knows its size, then call to length will short-cuircuit and it.next() will never execute, which means the iterator will not be consumed. This is the case for default concrete collection used by Iterator factory which is Array
val it = Iterator("a", "b", "c")
it.getClass.getSimpleName
// res6: Class[_ <: Iterator[String]] = class scala.collection.ArrayOps$ArrayIterator
however it is not true for BufferedSource. To workaround the issue consider creating an new iterator each time countLines is called
def countLines(fileName: String): Long = {
fromFile(fileName).getLines().length
}
println(s"In file $fileName is ${countLines(fileName)} lines")
println(s"In file $fileName is ${countLines(fileName)} lines")
// In file build.sbt is 22 lines
// In file build.sbt is 22 lines
Final point regarding value definitions and immutability. Consider
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
Here identifier foo is an immutable reference to mutable object.
I want to build an instance of a class Something by calling the function foo on this calss for every element in a list. e.g.
val list = List(1,2,3)
should result in a call with the same effect as:
val something = somethingBuilder.foo(1).foo(2).foo(3)
Is there a way to perform this?
As I understand your question, you can do :
val list = List(1,2,3)
val something = somethingBuilder
list.foreach(something.foo)
I am assuming that you care for the returned value of your builder call. Then following code will print Builder(6):
val list = List(1,2,3)
case class Builder(val i: Int){
def build(j: Int) = Builder(i+j)
}
val finalBuilder = list.foldLeft(Builder(0))(_.build(_))
println(finalBuilder)
If you only care for the side effect, maybe Rafael's solution is more adequate (although using the foldLeft will of course also trigger the side-effect).
This can be one of the solution.
val lst = List(1,2,3,4,5)
class Builder(val i: Int){
def Foo() {
println("Value is initialized: " + i)
}
}
lst.map( a => new Builder(a).Foo())
But the disclaimer, it does generates the list of empty lists as a side effect
print(lst.map( a => new Builder(a).Foo()))
The example code counts each word's occurrences in given input file:
object Main {
def main(args: Array[String]) {
val counts = new scala.collection.mutable.HashMap[String, Int]
val in = new Scanner(new File("input.txt"))
while (in.hasNext()) {
val s: String = in.next()
counts(s) = counts.getOrElse(s, 0) + 1 // Here!
}
print(counts)
}
}
Can the highlighted with comment line be rewritten using the getOrElseUpdate method?
P.S. I am only at the 4th part of the "Scala for the impatient", so please don't teach me now about functional Scala which, I am sure, can be more beautiful here.
Thanks.
If you look at the doc you'll see the next:
If given key is already in this map, returns associated value.
Otherwise, computes value from given expression op, stores with key in
map and returns that value.
, but you need to modify map anyway, so getOrElseUpdate is useless here.
You can define default value, which will return if key doesn't exist. And use it the next way:
import scala.collection.mutable.HashMap
object Main {
def main(args: Array[String]) {
val counts = HashMap[String, Int]().withDefaultValue(0)
val in = new Scanner(new File("input.txt"))
while (in.hasNext()) {
val s: String = in.next()
counts(s) += 1
}
print(counts)
}
}
I have tuple separated by a coma that looks like this:
("TRN_KEY", "88.330000;1;2")
I would like to add some more info to the second position.
For example:
I would like to add ;99;99 to the 88.330000;1;2 so that at the end it would look like:
(TRN_KEY, 88.330000;1;2;99;99)
One way is to de-compose your tuple and concat the additional string to the second element:
object MyObject {
val (first, second) = ("TRN_KEY","88.330000;1;2")
(first, second + ";3;4"))
}
Which yields:
res0: (String, String) = (TRN_KEY,88.330000;1;2;3;4)
Another way to go is copy to tuple with the new value using Tuple2.copy, as tuples are immutable by design.
You can not modify the data in place as Tuple2 is immutable.
An option would be to have a var and then use the copy method.
In Scala due to structural sharing this is a rather cheap and fast operation.
scala> var tup = ("TRN_KEY","88.330000;1;2")
tup: (String, String) = (TRN_KEY,88.330000;1;2)
scala> tup = tup.copy(_2 = tup._2 + "data")
tup: (String, String) = (TRN_KEY,88.330000;1;2data)
Here is a simple function that gets the job done. It takes a tuple and appends a string to the second element of the tuple.
def appendTup(tup:(String, String))(append:String):(String,String) = {
(tup._1, tup._2 + append)
}
Here is some code using it
val tup = ("TRN_KEY", "88.330000;1;2")
val tup2 = appendTup(tup)(";99;99")
println(tup2)
Here is my output
(TRN_KEY,88.330000;1;2;99;99)
If you really want to make it mutable you could use a case class such as:
case class newTup(col1: String, var col2: String)
val rec1 = newTup("TRN_KEY", "88.330000;1;2")
rec1.col2 = rec1.col2 + ";99;99"
rec1
res3: newTup = newTup(TRN_KEY,88.330000;1;2;99;99)
But, as mentioned above, it would be better to use .copy
I'm working on eclipse under ubuntu 12.04 with scala 2.10 and Akka 2.2.1.
// A and B are derived from Node
val algorithm = if(args(0) > 1)()=> new A else ()=> new B
/* alternative:
val algorithm = if(args(0) > 1) (()=> system.actorOf(Props(new A), name ="A") )
else (()=> system.actorOf(Props(new B),name="B"))
*/
// alternative : class work(algorithm: ()=>ActorRef, num:Int) {
class work(algorithm: ()=> Node, num: Int){
val a = Array.fill(num)(algorithm) // here I wanna create an array with num slots
// and objects of A or B in it
val rand = new Random(System.currentTimeMillis())
val randomNode = a(rand.nextInt(5))
def find (x:Int): Array[ActorRef]{....}
def receive = {
case Rumor =>
a.foreach(ref=> ref !Init(index, find(x), self))
randomNode ! Rumor
case _ => println(...)
}
}
update:
I create an array which contains Actor or ActorRef(I am not sure which one I am allowed to use in Akka). But eclipse reports on
case Rumor =>
a.foreach(ref=> ref !Init(index, find(x), self))
randomNode ! Rumor
I try several times, but it still does not work.
The Array constructor only accepts a length value, not a default value function. The post you reference is explaining how to build a custom data structure that accepts a default value generator.
What you're doing is equivalent to
val arr = new Array[Node](num)
val a = arr(algorithm)
so scala expects an integer index. It's complaining that it can't find a way to convert ()=>Node to an integer to access that index in the array.
To fill an array with a default value you could use Array.fill like this:
val a = Array.fill(num)(algorithm)
The correct way to create an array of actors should be:
val algorithm = if(args(0) > 1) ()=>context.actorOf(Props(new A))
else ()=>context.actorOf(Props(new B))
val a = Array.fill(num)(algorithm())